Tweaks for JWT auth

This commit is contained in:
Gregory Schier
2025-01-17 15:23:15 -08:00
parent 6ae0bc1ef6
commit 7a6ab60d30
11 changed files with 142 additions and 33 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@yaakapp/api", "name": "@yaakapp/api",
"version": "0.2.29", "version": "0.3.2",
"main": "lib/index.js", "main": "lib/index.js",
"typings": "./lib/index.d.ts", "typings": "./lib/index.d.ts",
"files": [ "files": [

View File

@@ -67,6 +67,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -81,6 +85,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -90,7 +98,15 @@ export type FormInputEditor = {
/** /**
* Placeholder for the text input * Placeholder for the text input
*/ */
placeholder?: string | null, language: EditorLanguage, name: string, placeholder?: string | null,
/**
* Don't show the editor gutter (line numbers, folds, etc.)
*/
hideGutter?: boolean,
/**
* Language for syntax highlighting
*/
language?: EditorLanguage, name: string,
/** /**
* Whether the user must fill in the argument * Whether the user must fill in the argument
*/ */
@@ -99,6 +115,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -121,6 +141,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -135,6 +159,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -153,6 +181,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -177,6 +209,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */

View File

@@ -67,6 +67,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -81,6 +85,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -90,7 +98,15 @@ export type FormInputEditor = {
/** /**
* Placeholder for the text input * Placeholder for the text input
*/ */
placeholder?: string | null, language: EditorLanguage, name: string, placeholder?: string | null,
/**
* Don't show the editor gutter (line numbers, folds, etc.)
*/
hideGutter?: boolean,
/**
* Language for syntax highlighting
*/
language?: EditorLanguage, name: string,
/** /**
* Whether the user must fill in the argument * Whether the user must fill in the argument
*/ */
@@ -99,6 +115,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -121,6 +141,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -135,6 +159,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -153,6 +181,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */
@@ -177,6 +209,10 @@ optional?: boolean,
* The label of the input * The label of the input
*/ */
label?: string, label?: string,
/**
* Visually hide the label of the input
*/
hideLabel?: boolean,
/** /**
* The default value * The default value
*/ */

View File

@@ -379,6 +379,10 @@ pub struct FormInputBase {
#[ts(optional)] #[ts(optional)]
pub label: Option<String>, pub label: Option<String>,
/// Visually hide the label of the input
#[ts(optional)]
pub hide_label: Option<bool>,
/// The default value /// The default value
#[ts(optional)] #[ts(optional)]
pub default_value: Option<String>, pub default_value: Option<String>,
@@ -430,7 +434,13 @@ pub struct FormInputEditor {
#[ts(optional = nullable)] #[ts(optional = nullable)]
pub placeholder: Option<String>, pub placeholder: Option<String>,
pub language: EditorLanguage, /// Don't show the editor gutter (line numbers, folds, etc.)
#[ts(optional)]
pub hide_gutter: Option<bool>,
/// Language for syntax highlighting
#[ts(optional)]
pub language: Option<EditorLanguage>,
} }
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]

View File

@@ -13,6 +13,7 @@ import { useCallback } from 'react';
import { useActiveRequest } from '../hooks/useActiveRequest'; import { useActiveRequest } from '../hooks/useActiveRequest';
import { useFolders } from '../hooks/useFolders'; import { useFolders } from '../hooks/useFolders';
import { useHttpRequests } from '../hooks/useHttpRequests'; import { useHttpRequests } from '../hooks/useHttpRequests';
import { capitalize } from '../lib/capitalize';
import { fallbackRequestName } from '../lib/fallbackRequestName'; import { fallbackRequestName } from '../lib/fallbackRequestName';
import { Checkbox } from './core/Checkbox'; import { Checkbox } from './core/Checkbox';
import { Editor } from './core/Editor/Editor'; import { Editor } from './core/Editor/Editor';
@@ -46,7 +47,7 @@ export function DynamicForm<T extends Record<string, string | boolean>>({
); );
return ( return (
<VStack space={3}> <VStack space={3} className="h-full overflow-auto">
{config.map((a, i) => { {config.map((a, i) => {
switch (a.type) { switch (a.type) {
case 'select': case 'select':
@@ -66,7 +67,7 @@ export function DynamicForm<T extends Record<string, string | boolean>>({
arg={a} arg={a}
useTemplating={useTemplating || false} useTemplating={useTemplating || false}
onChange={(v) => setDataAttr(a.name, v)} onChange={(v) => setDataAttr(a.name, v)}
value={data[a.name] ? String(data[a.name]) : ''} value={data[a.name] ? String(data[a.name]) : DYNAMIC_FORM_NULL_ARG}
/> />
); );
case 'editor': case 'editor':
@@ -77,7 +78,7 @@ export function DynamicForm<T extends Record<string, string | boolean>>({
arg={a} arg={a}
useTemplating={useTemplating || false} useTemplating={useTemplating || false}
onChange={(v) => setDataAttr(a.name, v)} onChange={(v) => setDataAttr(a.name, v)}
value={data[a.name] ? String(data[a.name]) : ''} value={data[a.name] ? String(data[a.name]) : DYNAMIC_FORM_NULL_ARG}
/> />
); );
case 'checkbox': case 'checkbox':
@@ -137,7 +138,7 @@ function TextArg({
<Input <Input
name={arg.name} name={arg.name}
onChange={handleChange} onChange={handleChange}
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? '' : value} defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
required={!arg.optional} required={!arg.optional}
type={arg.password ? 'password' : 'text'} type={arg.password ? 'password' : 'text'}
label={arg.label ?? arg.name} label={arg.label ?? arg.name}
@@ -173,8 +174,13 @@ function EditorArg({
const id = `input-${arg.name}`; const id = `input-${arg.name}`;
return ( return (
<div className="w-full grid grid-rows-[auto_minmax(0,1fr)]"> <div className=" w-full grid grid-cols-1 grid-rows-[auto_minmax(0,1fr)]">
<Label htmlFor={id} optional={arg.optional}> <Label
htmlFor={id}
optional={arg.optional}
visuallyHidden={arg.hideLabel}
otherTags={arg.language ? [capitalize(arg.language)] : undefined}
>
{arg.label} {arg.label}
</Label> </Label>
<Editor <Editor
@@ -182,15 +188,17 @@ function EditorArg({
className={classNames( className={classNames(
'border border-border rounded-md overflow-hidden px-2 py-1.5', 'border border-border rounded-md overflow-hidden px-2 py-1.5',
'focus-within:border-border-focus', 'focus-within:border-border-focus',
'max-h-[15rem]', // So it doesn't take up too much space
)} )}
language={arg.language} language={arg.language}
onChange={handleChange} onChange={handleChange}
heightMode="auto" heightMode="auto"
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? '' : value} defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
placeholder={arg.placeholder ?? arg.defaultValue ?? ''} placeholder={arg.placeholder ?? arg.defaultValue ?? ''}
useTemplating={useTemplating} useTemplating={useTemplating}
stateKey={stateKey} stateKey={stateKey}
forceUpdateKey={stateKey} forceUpdateKey={stateKey}
hideGutter
/> />
</div> </div>
); );
@@ -210,6 +218,7 @@ function SelectArg({
label={arg.label ?? arg.name} label={arg.label ?? arg.name}
name={arg.name} name={arg.name}
onChange={onChange} onChange={onChange}
hideLabel={arg.hideLabel}
value={value} value={value}
options={[ options={[
...arg.options.map((a) => ({ ...arg.options.map((a) => ({

View File

@@ -97,6 +97,7 @@ export function SettingsGeneral() {
</Heading> </Heading>
<VStack className="mt-1 w-full" space={3}> <VStack className="mt-1 w-full" space={3}>
<PlainInput <PlainInput
required
size="sm" size="sm"
name="requestTimeout" name="requestTimeout"
label="Request Timeout (ms)" label="Request Timeout (ms)"

View File

@@ -68,7 +68,7 @@ export function SettingsProxy() {
}} }}
/> />
</HStack> </HStack>
<Separator className="my-6"/> <Separator className="my-6" />
<Checkbox <Checkbox
checked={settings.proxy.auth != null} checked={settings.proxy.auth != null}
title="Enable authentication" title="Enable authentication"
@@ -83,6 +83,7 @@ export function SettingsProxy() {
{settings.proxy.auth != null && ( {settings.proxy.auth != null && (
<HStack space={1.5}> <HStack space={1.5}>
<PlainInput <PlainInput
required
size="sm" size="sm"
label="User" label="User"
placeholder="myUser" placeholder="myUser"

View File

@@ -44,6 +44,7 @@ export type InputProps = Pick<
validate?: boolean | ((v: string) => boolean); validate?: boolean | ((v: string) => boolean);
required?: boolean; required?: boolean;
wrapLines?: boolean; wrapLines?: boolean;
multiLine?: boolean;
stateKey: EditorProps['stateKey']; stateKey: EditorProps['stateKey'];
}; };
@@ -73,6 +74,7 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
validate, validate,
readOnly, readOnly,
stateKey, stateKey,
multiLine,
...props ...props
}: InputProps, }: InputProps,
ref, ref,
@@ -144,7 +146,8 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
<Label <Label
htmlFor={id.current} htmlFor={id.current}
optional={!required} optional={!required}
className={classNames(labelClassName, hideLabel && 'sr-only')} visuallyHidden={hideLabel}
className={classNames(labelClassName)}
> >
{label} {label}
</Label> </Label>
@@ -174,9 +177,11 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
<Editor <Editor
ref={editorRef} ref={editorRef}
id={id.current} id={id.current}
singleLine hideGutter
singleLine={!multiLine}
stateKey={stateKey} stateKey={stateKey}
wrapLines={wrapLines} wrapLines={wrapLines}
heightMode="auto"
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
type={type === 'password' && !obscured ? 'text' : type} type={type === 'password' && !obscured ? 'text' : type}
defaultValue={defaultValue} defaultValue={defaultValue}
@@ -185,7 +190,7 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
onChange={handleChange} onChange={handleChange}
onPaste={onPaste} onPaste={onPaste}
onPasteOverwrite={onPasteOverwrite} onPasteOverwrite={onPasteOverwrite}
className={editorClassName} className={classNames(editorClassName, multiLine && 'py-1.5')}
onFocus={handleFocus} onFocus={handleFocus}
onBlur={handleBlur} onBlur={handleBlur}
readOnly={readOnly} readOnly={readOnly}

View File

@@ -6,21 +6,33 @@ export function Label({
className, className,
optional, optional,
children, children,
visuallyHidden,
otherTags = [],
...props ...props
}: HTMLAttributes<HTMLLabelElement> & { htmlFor: string; optional?: boolean }) { }: HTMLAttributes<HTMLLabelElement> & {
htmlFor: string;
optional?: boolean;
otherTags?: string[];
visuallyHidden?: boolean;
}) {
const tags = optional ? ['optional', ...otherTags] : otherTags;
return ( return (
<label <label
className={classNames(className, 'text-text-subtle whitespace-nowrap flex items-center gap-1')} className={classNames(
className,
visuallyHidden && 'sr-only',
'flex-shrink-0',
'text-text-subtle whitespace-nowrap flex items-center gap-1',
)}
htmlFor={htmlFor} htmlFor={htmlFor}
{...props} {...props}
> >
{children} {children}
{optional ? ( {tags.map((tag, i) => (
<> <span key={i} className="text-xs text-text-subtlest">
{' '} ({tag})
<span className="text-xs text-text-subtlest">(optional)</span> </span>
</> ))}
) : null}
</label> </label>
); );
} }

View File

@@ -98,7 +98,12 @@ export function PlainInput({
labelPosition === 'top' && 'flex-row gap-0.5', labelPosition === 'top' && 'flex-row gap-0.5',
)} )}
> >
<Label htmlFor={id} className={classNames(labelClassName, 'flex-shrink-0', hideLabel && 'sr-only')}> <Label
htmlFor={id}
className={labelClassName}
visuallyHidden={hideLabel}
optional={!required}
>
{label} {label}
</Label> </Label>
<HStack <HStack

View File

@@ -5,6 +5,7 @@ import { useOsInfo } from '../../hooks/useOsInfo';
import { trackEvent } from '../../lib/analytics'; import { trackEvent } from '../../lib/analytics';
import type { ButtonProps } from './Button'; import type { ButtonProps } from './Button';
import { Button } from './Button'; import { Button } from './Button';
import { Label } from './Label';
import type { RadioDropdownItem } from './RadioDropdown'; import type { RadioDropdownItem } from './RadioDropdown';
import { RadioDropdown } from './RadioDropdown'; import { RadioDropdown } from './RadioDropdown';
import { HStack } from './Stacks'; import { HStack } from './Stacks';
@@ -61,16 +62,9 @@ export function Select<T extends string>({
labelPosition === 'top' && 'flex-row gap-0.5', labelPosition === 'top' && 'flex-row gap-0.5',
)} )}
> >
<label <Label htmlFor={id} visuallyHidden={hideLabel} className={labelClassName}>
htmlFor={id}
className={classNames(
labelClassName,
'text-text-subtle whitespace-nowrap',
hideLabel && 'sr-only',
)}
>
{label} {label}
</label> </Label>
{osInfo?.osType === 'macos' ? ( {osInfo?.osType === 'macos' ? (
<HStack <HStack
space={2} space={2}