diff --git a/src-web/components/BasicAuth.tsx b/src-web/components/BasicAuth.tsx deleted file mode 100644 index ea6d2b41..00000000 --- a/src-web/components/BasicAuth.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; -import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; -import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models'; -import { Input } from './core/Input'; -import { VStack } from './core/Stacks'; - -interface Props { - request: T; -} - -export function BasicAuth({ request }: Props) { - const updateHttpRequest = useUpdateAnyHttpRequest(); - const updateGrpcRequest = useUpdateAnyGrpcRequest(); - - return ( - - { - if (request.model === 'http_request') { - updateHttpRequest.mutate({ - id: request.id, - update: (r: HttpRequest) => ({ - ...r, - authentication: { password: r.authentication.password, username }, - }), - }); - } else { - updateGrpcRequest.mutate({ - id: request.id, - update: (r: GrpcRequest) => ({ - ...r, - authentication: { password: r.authentication.password, username }, - }), - }); - } - }} - /> - { - if (request.model === 'http_request') { - updateHttpRequest.mutate({ - id: request.id, - update: (r: HttpRequest) => ({ - ...r, - authentication: { username: r.authentication.username, password }, - }), - }); - } else { - updateGrpcRequest.mutate({ - id: request.id, - update: (r: GrpcRequest) => ({ - ...r, - authentication: { username: r.authentication.username, password }, - }), - }); - } - }} - /> - - ); -} diff --git a/src-web/components/BearerAuth.tsx b/src-web/components/BearerAuth.tsx deleted file mode 100644 index 1dc31499..00000000 --- a/src-web/components/BearerAuth.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; -import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest'; -import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models'; -import { Input } from './core/Input'; -import { VStack } from './core/Stacks'; - -interface Props { - request: T; -} - -export function BearerAuth({ request }: Props) { - const updateHttpRequest = useUpdateAnyHttpRequest(); - const updateGrpcRequest = useUpdateAnyGrpcRequest(); - - return ( - - { - if (request.model === 'http_request') { - updateHttpRequest.mutate({ - id: request.id ?? null, - update: (r: HttpRequest) => ({ - ...r, - authentication: { token }, - }), - }); - } else { - updateGrpcRequest.mutate({ - id: request.id ?? null, - update: (r: GrpcRequest) => ({ - ...r, - authentication: { token }, - }), - }); - } - }} - /> - - ); -} diff --git a/src-web/components/DynamicForm.tsx b/src-web/components/DynamicForm.tsx index dedca04e..6609a111 100644 --- a/src-web/components/DynamicForm.tsx +++ b/src-web/components/DynamicForm.tsx @@ -34,7 +34,7 @@ interface Props { inputs: FormInput[] | undefined | null; onChange: (value: T) => void; data: T; - useTemplating?: boolean; + autocompleteFunctions?: boolean; autocompleteVariables?: boolean; stateKey: string; disabled?: boolean; @@ -44,8 +44,8 @@ export function DynamicForm>({ inputs, data, onChange, - useTemplating, autocompleteVariables, + autocompleteFunctions, stateKey, disabled, }: Props) { @@ -62,7 +62,7 @@ export function DynamicForm>({ inputs={inputs} setDataAttr={setDataAttr} stateKey={stateKey} - useTemplating={useTemplating} + autocompleteFunctions={autocompleteFunctions} autocompleteVariables={autocompleteVariables} data={data} /> @@ -71,13 +71,16 @@ export function DynamicForm>({ function FormInputs>({ inputs, + autocompleteFunctions, autocompleteVariables, stateKey, - useTemplating, setDataAttr, data, disabled, -}: Pick, 'inputs' | 'useTemplating' | 'autocompleteVariables' | 'stateKey' | 'data'> & { +}: Pick< + Props, + 'inputs' | 'autocompleteFunctions' | 'autocompleteVariables' | 'stateKey' | 'data' +> & { setDataAttr: (name: string, value: JsonPrimitive) => void; disabled?: boolean; }) { @@ -112,7 +115,7 @@ function FormInputs>({ key={i} stateKey={stateKey} arg={input} - useTemplating={useTemplating || false} + autocompleteFunctions={autocompleteFunctions || false} autocompleteVariables={autocompleteVariables || false} onChange={(v) => setDataAttr(input.name, v)} value={ @@ -126,7 +129,7 @@ function FormInputs>({ key={i} stateKey={stateKey} arg={input} - useTemplating={useTemplating || false} + autocompleteFunctions={autocompleteFunctions || false} autocompleteVariables={autocompleteVariables || false} onChange={(v) => setDataAttr(input.name, v)} value={ @@ -175,7 +178,7 @@ function FormInputs>({ inputs={input.inputs} setDataAttr={setDataAttr} stateKey={stateKey} - useTemplating={useTemplating} + autocompleteFunctions={autocompleteFunctions || false} autocompleteVariables={autocompleteVariables} /> @@ -195,7 +198,7 @@ function FormInputs>({ inputs={input.inputs} setDataAttr={setDataAttr} stateKey={stateKey} - useTemplating={useTemplating} + autocompleteFunctions={autocompleteFunctions || false} autocompleteVariables={autocompleteVariables} /> @@ -212,14 +215,14 @@ function TextArg({ arg, onChange, value, - useTemplating, + autocompleteFunctions, autocompleteVariables, stateKey, }: { arg: FormInputText; value: string; onChange: (v: string) => void; - useTemplating: boolean; + autocompleteFunctions: boolean; autocompleteVariables: boolean; stateKey: string; }) { @@ -237,7 +240,7 @@ function TextArg({ hideLabel={arg.label == null} placeholder={arg.placeholder ?? undefined} autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined} - useTemplating={useTemplating} + autocompleteFunctions={autocompleteFunctions} autocompleteVariables={autocompleteVariables} stateKey={stateKey} forceUpdateKey={stateKey} @@ -249,14 +252,14 @@ function EditorArg({ arg, onChange, value, - useTemplating, + autocompleteFunctions, autocompleteVariables, stateKey, }: { arg: FormInputEditor; value: string; onChange: (v: string) => void; - useTemplating: boolean; + autocompleteFunctions: boolean; autocompleteVariables: boolean; stateKey: string; }) { @@ -290,7 +293,7 @@ function EditorArg({ heightMode="auto" defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value} placeholder={arg.placeholder ?? undefined} - useTemplating={useTemplating} + autocompleteFunctions={autocompleteFunctions} autocompleteVariables={autocompleteVariables} stateKey={stateKey} forceUpdateKey={forceUpdateKey} diff --git a/src-web/components/EnvironmentEditDialog.tsx b/src-web/components/EnvironmentEditDialog.tsx index a0630b05..1a7f4092 100644 --- a/src-web/components/EnvironmentEditDialog.tsx +++ b/src-web/components/EnvironmentEditDialog.tsx @@ -116,7 +116,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) { }; const EnvironmentEditor = function ({ - environment, + environment: activeEnvironment, className, }: { environment: Environment; @@ -127,8 +127,8 @@ const EnvironmentEditor = function ({ key: 'environmentValueVisibility', fallback: true, }); - const { subEnvironments } = useEnvironments(); - const updateEnvironment = useUpdateEnvironment(environment?.id ?? null); + const { allEnvironments } = useEnvironments(); + const updateEnvironment = useUpdateEnvironment(activeEnvironment?.id ?? null); const handleChange = useCallback( (variables) => updateEnvironment.mutate({ variables }), [updateEnvironment], @@ -136,26 +136,28 @@ const EnvironmentEditor = function ({ // Gather a list of env names from other environments, to help the user get them aligned const nameAutocomplete = useMemo(() => { - const allVariableNames = - environment == null - ? [] // Nothing to autocomplete if we're in the base environment - : subEnvironments - .filter((e) => e.environmentId != null) - .flatMap((e) => e.variables.map((v) => v.name)); + const options: GenericCompletionOption[] = []; + const isBaseEnv = activeEnvironment.environmentId == null; + if (isBaseEnv) { + return { options }; + } - // Filter out empty strings and variables that already exist - const variableNames = allVariableNames.filter( - (name) => name != '' && !environment.variables.find((v) => v.name === name), - ); - const uniqueVariableNames = [...new Set(variableNames)]; - const options = uniqueVariableNames.map( - (name): GenericCompletionOption => ({ + const allVariables = allEnvironments.flatMap((e) => e?.variables); + const allVariableNames = new Set(allVariables.map((v) => v?.name)); + for (const name of allVariableNames) { + const containingEnvs = allEnvironments.filter((e) => + e.variables.some((v) => v.name === name), + ); + const isAlreadyInActive = containingEnvs.find((e) => e.id === activeEnvironment.id); + if (isAlreadyInActive) continue; + options.push({ label: name, type: 'constant', - }), - ); + detail: containingEnvs.map((e) => e.name).join(', '), + }); + } return { options }; - }, [subEnvironments, environment]); + }, [activeEnvironment.environmentId, activeEnvironment.id, allEnvironments]); const validateName = useCallback((name: string) => { // Empty just means the variable doesn't have a name yet, and is unusable @@ -167,7 +169,7 @@ const EnvironmentEditor = function ({ -
{environment?.name}
+
{activeEnvironment?.name}
diff --git a/src-web/components/FormMultipartEditor.tsx b/src-web/components/FormMultipartEditor.tsx index 689d4027..bc6aa97b 100644 --- a/src-web/components/FormMultipartEditor.tsx +++ b/src-web/components/FormMultipartEditor.tsx @@ -40,8 +40,10 @@ export function FormMultipartEditor({ request, forceUpdateKey, onChange }: Props return ( diff --git a/src-web/components/GrpcEditor.tsx b/src-web/components/GrpcEditor.tsx index 80cca192..f2c8d432 100644 --- a/src-web/components/GrpcEditor.tsx +++ b/src-web/components/GrpcEditor.tsx @@ -181,8 +181,8 @@ export function GrpcEditor({
diff --git a/src-web/components/WebsocketRequestPane.tsx b/src-web/components/WebsocketRequestPane.tsx index 58acdd63..bd26e5a7 100644 --- a/src-web/components/WebsocketRequestPane.tsx +++ b/src-web/components/WebsocketRequestPane.tsx @@ -301,7 +301,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque = { }; export interface EditorProps { - id?: string; - readOnly?: boolean; - disabled?: boolean; - type?: 'text' | 'password'; - className?: string; - heightMode?: 'auto' | 'full'; - language?: EditorLanguage | 'pairs' | 'url'; - forceUpdateKey?: string | number; + actions?: ReactNode; autoFocus?: boolean; autoSelect?: boolean; + autocomplete?: GenericCompletionConfig; + autocompleteFunctions?: boolean; + autocompleteVariables?: boolean; + className?: string; defaultValue?: string | null; - placeholder?: string; - tooltipContainer?: HTMLElement; - useTemplating?: boolean; + disableTabIndent?: boolean; + disabled?: boolean; + extraExtensions?: Extension[]; + forceUpdateKey?: string | number; + format?: (v: string) => Promise; + heightMode?: 'auto' | 'full'; + hideGutter?: boolean; + id?: string; + language?: EditorLanguage | 'pairs' | 'url'; + onBlur?: () => void; onChange?: (value: string) => void; + onFocus?: () => void; + onKeyDown?: (e: KeyboardEvent) => void; onPaste?: (value: string) => void; onPasteOverwrite?: (e: ClipboardEvent, value: string) => void; - onFocus?: () => void; - onBlur?: () => void; - onKeyDown?: (e: KeyboardEvent) => void; + placeholder?: string; + readOnly?: boolean; singleLine?: boolean; - wrapLines?: boolean; - disableTabIndent?: boolean; - format?: (v: string) => Promise; - autocomplete?: GenericCompletionConfig; - autocompleteVariables?: boolean; - extraExtensions?: Extension[]; - actions?: ReactNode; - hideGutter?: boolean; stateKey: string | null; + tooltipContainer?: HTMLElement; + type?: 'text' | 'password'; + wrapLines?: boolean; } const stateFields = { history: historyField, folds: foldState }; @@ -94,34 +94,34 @@ const emptyExtension: Extension = []; export const Editor = forwardRef(function Editor( { - readOnly, - type, - heightMode, - language, + actions, autoFocus, autoSelect, - placeholder, - useTemplating, + autocomplete, + autocompleteFunctions, + autocompleteVariables, + className, defaultValue, + disableTabIndent, + disabled, + extraExtensions, forceUpdateKey, + format, + heightMode, + hideGutter, + language, + onBlur, onChange, + onFocus, + onKeyDown, onPaste, onPasteOverwrite, - onFocus, - onBlur, - onKeyDown, - className, - disabled, + placeholder, + readOnly, singleLine, - format, - autocomplete, - extraExtensions, - autocompleteVariables, - actions, - wrapLines, - disableTabIndent, - hideGutter, stateKey, + type, + wrapLines, }: EditorProps, ref, ) { @@ -129,6 +129,7 @@ export const Editor = forwardRef(function E const allEnvironmentVariables = useActiveEnvironmentVariables(); const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables; + const useTemplating = !!(autocompleteFunctions || autocompleteVariables || autocomplete); if (settings && wrapLines === undefined) { wrapLines = settings.editorSoftWrap; @@ -340,16 +341,19 @@ export const Editor = forwardRef(function E [focusParamValue], ); - const completionOptions = useTemplateFunctionCompletionOptions(onClickFunction); + const completionOptions = useTemplateFunctionCompletionOptions( + onClickFunction, + !!autocompleteFunctions, + ); // Update the language extension when the language changes useEffect(() => { if (cm.current === null) return; const { view, languageCompartment } = cm.current; const ext = getLanguageExtension({ + useTemplating, language, environmentVariables, - useTemplating, autocomplete, completionOptions, onClickVariable, @@ -360,13 +364,13 @@ export const Editor = forwardRef(function E }, [ language, autocomplete, - useTemplating, environmentVariables, onClickFunction, onClickVariable, onClickMissingVariable, onClickPathParameter, completionOptions, + useTemplating, ]); // Initialize the editor when ref mounts @@ -381,8 +385,8 @@ export const Editor = forwardRef(function E try { const languageCompartment = new Compartment(); const langExt = getLanguageExtension({ - language, useTemplating, + language, completionOptions, autocomplete, environmentVariables, diff --git a/src-web/components/core/Editor/extensions.ts b/src-web/components/core/Editor/extensions.ts index 056fdd1e..092b432b 100644 --- a/src-web/components/core/Editor/extensions.ts +++ b/src-web/components/core/Editor/extensions.ts @@ -91,8 +91,8 @@ const syntaxExtensions: Record, LanguageSup const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ['json', 'javascript', 'graphql']; export function getLanguageExtension({ + useTemplating, language = 'text', - useTemplating = false, environmentVariables, autocomplete, onClickVariable, @@ -100,12 +100,13 @@ export function getLanguageExtension({ onClickPathParameter, completionOptions, }: { + useTemplating: boolean; environmentVariables: EnvironmentVariable[]; onClickVariable: (option: EnvironmentVariable, tagValue: string, startPos: number) => void; onClickMissingVariable: (name: string, tagValue: string, startPos: number) => void; onClickPathParameter: (name: string) => void; completionOptions: TwigCompletionOption[]; -} & Pick) { +} & Pick) { const extraExtensions: Extension[] = []; if (language === 'url') { diff --git a/src-web/components/core/Input.tsx b/src-web/components/core/Input.tsx index fc92560d..ab5c7cb3 100644 --- a/src-web/components/core/Input.tsx +++ b/src-web/components/core/Input.tsx @@ -13,13 +13,13 @@ import { HStack } from './Stacks'; export type InputProps = Pick< EditorProps, | 'language' - | 'useTemplating' | 'autocomplete' | 'forceUpdateKey' | 'disabled' | 'autoFocus' | 'autoSelect' | 'autocompleteVariables' + | 'autocompleteFunctions' | 'onKeyDown' | 'readOnly' > & { diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index 1fb82078..6f678a77 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -43,6 +43,7 @@ export type PairEditorProps = { className?: string; forceUpdateKey?: string; nameAutocomplete?: GenericCompletionConfig; + nameAutocompleteFunctions?: boolean; nameAutocompleteVariables?: boolean; namePlaceholder?: string; nameValidate?: InputProps['validate']; @@ -51,6 +52,7 @@ export type PairEditorProps = { pairs: Pair[]; stateKey: InputProps['stateKey']; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; + valueAutocompleteFunctions?: boolean; valueAutocompleteVariables?: boolean; valuePlaceholder?: string; valueType?: 'text' | 'password'; @@ -82,6 +84,7 @@ export const PairEditor = forwardRef(function Pa className, forceUpdateKey, nameAutocomplete, + nameAutocompleteFunctions, nameAutocompleteVariables, namePlaceholder, nameValidate, @@ -89,6 +92,7 @@ export const PairEditor = forwardRef(function Pa onChange, pairs: originalPairs, valueAutocomplete, + valueAutocompleteFunctions, valueAutocompleteVariables, valuePlaceholder, valueType, @@ -237,6 +241,7 @@ export const PairEditor = forwardRef(function Pa index={i} isLast={isLast} nameAutocomplete={nameAutocomplete} + nameAutocompleteFunctions={nameAutocompleteFunctions} nameAutocompleteVariables={nameAutocompleteVariables} namePlaceholder={namePlaceholder} nameValidate={nameValidate} @@ -248,6 +253,7 @@ export const PairEditor = forwardRef(function Pa pair={p} stateKey={stateKey} valueAutocomplete={valueAutocomplete} + valueAutocompleteFunctions={valueAutocompleteFunctions} valueAutocompleteVariables={valueAutocompleteVariables} valuePlaceholder={valuePlaceholder} valueType={valueType} @@ -291,8 +297,10 @@ type PairEditorRowProps = { | 'nameAutocompleteVariables' | 'namePlaceholder' | 'nameValidate' + | 'nameAutocompleteFunctions' | 'stateKey' | 'valueAutocomplete' + | 'valueAutocompleteFunctions' | 'valueAutocompleteVariables' | 'valuePlaceholder' | 'valueType' @@ -309,9 +317,10 @@ function PairEditorRow({ index, isLast, nameAutocomplete, - nameAutocompleteVariables, namePlaceholder, nameValidate, + nameAutocompleteFunctions, + nameAutocompleteVariables, onChange, onDelete, onEnd, @@ -320,6 +329,7 @@ function PairEditorRow({ pair, stateKey, valueAutocomplete, + valueAutocompleteFunctions, valueAutocompleteVariables, valuePlaceholder, valueType, @@ -486,7 +496,6 @@ function PairEditorRow({ )}
@@ -534,7 +544,6 @@ function PairEditorRow({ )} @@ -695,7 +705,7 @@ function MultilineEditDialog({ language={language} onChange={setValue} stateKey={null} - useTemplating + autocompleteFunctions autocompleteVariables />
diff --git a/src-web/hooks/useTemplateFunctions.ts b/src-web/hooks/useTemplateFunctions.ts index b57f5afd..1cbb1db5 100644 --- a/src-web/hooks/useTemplateFunctions.ts +++ b/src-web/hooks/useTemplateFunctions.ts @@ -11,9 +11,13 @@ const templateFunctionsAtom = atom([]); export function useTemplateFunctionCompletionOptions( onClick: (fn: TemplateFunction, ragTag: string, pos: number) => void, + enabled: boolean, ) { const templateFunctions = useAtomValue(templateFunctionsAtom); return useMemo(() => { + if (!enabled) { + return []; + } return ( templateFunctions.map((fn) => { const NUM_ARGS = 2; @@ -35,7 +39,7 @@ export function useTemplateFunctionCompletionOptions( }; }) ?? [] ); - }, [onClick, templateFunctions]); + }, [enabled, onClick, templateFunctions]); } export function useSubscribeTemplateFunctions() {