diff --git a/src-web/components/EnvironmentEditDialog.tsx b/src-web/components/EnvironmentEditDialog.tsx index 52c43bc8..e8f0c782 100644 --- a/src-web/components/EnvironmentEditDialog.tsx +++ b/src-web/components/EnvironmentEditDialog.tsx @@ -5,6 +5,7 @@ import { useActiveWorkspace } from '../hooks/useActiveWorkspace'; import { useCreateEnvironment } from '../hooks/useCreateEnvironment'; import { useDeleteEnvironment } from '../hooks/useDeleteEnvironment'; import { useEnvironments } from '../hooks/useEnvironments'; +import { useKeyValue } from '../hooks/useKeyValue'; import { usePrompt } from '../hooks/usePrompt'; import { useUpdateEnvironment } from '../hooks/useUpdateEnvironment'; import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace'; @@ -59,14 +60,16 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) { setSelectedEnvironmentId(null)} - className="group" environment={null} rightSlot={ } @@ -113,6 +116,11 @@ const EnvironmentEditor = function ({ workspace: Workspace; className?: string; }) { + const valueVisibility = useKeyValue({ + namespace: 'global', + key: 'environmentValueVisibility', + fallback: true, + }); const environments = useEnvironments(); const updateEnvironment = useUpdateEnvironment(environment?.id ?? null); const updateWorkspace = useUpdateWorkspace(workspace.id); @@ -164,8 +172,17 @@ const EnvironmentEditor = function ({ return ( - +
{environment?.name ?? 'Global Variables'}
+ { + return valueVisibility.set((v) => !v); + }} + />
@@ -225,7 +243,7 @@ function SidebarButton({ size="xs" className={classNames( 'w-full', - active ? 'text-gray-800' : 'text-gray-600 hover:text-gray-700', + active ? 'text-gray-800 bg-highlightSecondary' : 'text-gray-600 hover:text-gray-700', )} justify="start" onClick={onClick} diff --git a/src-web/components/core/Editor/Editor.tsx b/src-web/components/core/Editor/Editor.tsx index ab390f2d..f551cb71 100644 --- a/src-web/components/core/Editor/Editor.tsx +++ b/src-web/components/core/Editor/Editor.tsx @@ -9,7 +9,6 @@ import { cloneElement, forwardRef, isValidElement, - memo, useCallback, useEffect, useImperativeHandle, @@ -57,7 +56,7 @@ export interface EditorProps { actions?: ReactNode; } -const _Editor = forwardRef(function Editor( +export const Editor = forwardRef(function Editor( { readOnly, type = 'text', @@ -295,8 +294,6 @@ const _Editor = forwardRef(function Editor( ); }); -export const Editor = memo(_Editor); - function getExtensions({ container, readOnly, diff --git a/src-web/components/core/Input.tsx b/src-web/components/core/Input.tsx index 861dfbdf..b143ce57 100644 --- a/src-web/components/core/Input.tsx +++ b/src-web/components/core/Input.tsx @@ -2,6 +2,7 @@ import classNames from 'classnames'; import type { EditorView } from 'codemirror'; import type { HTMLAttributes, ReactNode } from 'react'; import { forwardRef, useCallback, useMemo, useRef, useState } from 'react'; +import { useStateSyncDefault } from '../../hooks/useStateSyncDefault'; import type { EditorProps } from './Editor'; import { Editor } from './Editor'; import { IconButton } from './IconButton'; @@ -69,7 +70,7 @@ export const Input = forwardRef(function Inp }: InputProps, ref, ) { - const [obscured, setObscured] = useState(type === 'password'); + const [obscured, setObscured] = useStateSyncDefault(type === 'password'); const [currentValue, setCurrentValue] = useState(defaultValue ?? ''); const [focused, setFocused] = useState(false); @@ -181,9 +182,10 @@ export const Input = forwardRef(function Inp setObscured((o) => !o)} /> )} diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index 5c6a584a..e3a3f33a 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -22,6 +22,7 @@ export type PairEditorProps = { className?: string; namePlaceholder?: string; valuePlaceholder?: string; + valueType?: 'text' | 'password'; nameAutocomplete?: GenericCompletionConfig; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; nameAutocompleteVariables?: boolean; @@ -51,6 +52,7 @@ export const PairEditor = memo(function PairEditor({ nameAutocompleteVariables, namePlaceholder, nameValidate, + valueType, onChange, pairs: originalPairs, valueAutocomplete, @@ -176,6 +178,7 @@ export const PairEditor = memo(function PairEditor({ allowFileValues={allowFileValues} nameAutocompleteVariables={nameAutocompleteVariables} valueAutocompleteVariables={valueAutocompleteVariables} + valueType={valueType} forceFocusPairId={forceFocusPairId} forceUpdateKey={forceUpdateKey} nameAutocomplete={nameAutocomplete} @@ -218,6 +221,7 @@ type FormRowProps = { | 'valueAutocomplete' | 'nameAutocompleteVariables' | 'valueAutocompleteVariables' + | 'valueType' | 'namePlaceholder' | 'valuePlaceholder' | 'nameValidate' @@ -246,6 +250,7 @@ const FormRow = memo(function FormRow({ valueAutocompleteVariables, valuePlaceholder, valueValidate, + valueType, }: FormRowProps) { const { id } = pairContainer; const ref = useRef(null); @@ -397,6 +402,7 @@ const FormRow = memo(function FormRow({ name="value" onChange={handleChangeValueText} onFocus={handleFocus} + type={isLast ? 'text' : valueType} placeholder={valuePlaceholder ?? 'value'} autocomplete={valueAutocomplete?.(pairContainer.pair.name)} autocompleteVariables={valueAutocompleteVariables} diff --git a/src-web/hooks/useStateSyncDefault.ts b/src-web/hooks/useStateSyncDefault.ts new file mode 100644 index 00000000..fc2be01a --- /dev/null +++ b/src-web/hooks/useStateSyncDefault.ts @@ -0,0 +1,12 @@ +import { useEffect, useState } from 'react'; + +/** + * Like useState, except it will update the value when the default value changes + */ +export function useStateSyncDefault(defaultValue: T) { + const [value, setValue] = useState(defaultValue); + useEffect(() => { + setValue(defaultValue); + }, [defaultValue]); + return [value, setValue] as const; +}