Obscure text

This commit is contained in:
Gregory Schier
2023-03-29 10:16:51 -07:00
parent 0f58986b4c
commit cd5ae6691c
12 changed files with 54 additions and 29 deletions

View File

@@ -7,23 +7,22 @@
#[macro_use] #[macro_use]
extern crate objc; extern crate objc;
use base64::Engine;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::env::current_dir; use std::env::current_dir;
use std::fs::create_dir_all; use std::fs::create_dir_all;
use http::header::{HeaderName, ACCEPT, USER_AGENT}; use base64::Engine;
use http::{HeaderMap, HeaderValue, Method}; use http::{HeaderMap, HeaderValue, Method};
use http::header::{ACCEPT, HeaderName, USER_AGENT};
use reqwest::redirect::Policy; use reqwest::redirect::Policy;
use serde::Serialize; use sqlx::{Pool, Sqlite};
use sqlx::migrate::Migrator; use sqlx::migrate::Migrator;
use sqlx::sqlite::SqlitePoolOptions; use sqlx::sqlite::SqlitePoolOptions;
use sqlx::types::{Json, JsonValue}; use sqlx::types::Json;
use sqlx::{Pool, Sqlite}; use tauri::{AppHandle, Menu, MenuItem, State, Submenu, TitleBarStyle, Window, Wry};
use tauri::regex::Regex;
use tauri::{AppHandle, Menu, MenuItem, Runtime, State, Submenu, TitleBarStyle, Window, Wry};
use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent}; use tauri::{CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, WindowEvent};
use tauri::regex::Regex;
use tokio::sync::Mutex; use tokio::sync::Mutex;
use window_ext::WindowExt; use window_ext::WindowExt;

View File

@@ -29,6 +29,7 @@ export function BasicAuth({ requestId, authentication }: Props) {
label="Password" label="Password"
name="password" name="password"
size="sm" size="sm"
type="password"
defaultValue={`${authentication.password}`} defaultValue={`${authentication.password}`}
onChange={(password: string) => { onChange={(password: string) => {
updateRequest.mutate((r) => ({ updateRequest.mutate((r) => ({

View File

@@ -108,7 +108,7 @@ export const RequestPane = memo(function RequestPane({ style, fullHeight, classN
}, },
}, },
}, },
{ value: 'params', label: 'URL Params' }, // { value: 'params', label: 'URL Params' },
{ value: 'headers', label: 'Headers' }, { value: 'headers', label: 'Headers' },
], ],
[ [

View File

@@ -52,7 +52,7 @@ export const UrlBar = memo(function UrlBar({ id: requestId, url, method, classNa
} }
rightSlot={ rightSlot={
<IconButton <IconButton
size="xs" size="sm"
title="Send Request" title="Send Request"
type="submit" type="submit"
color="custom" color="custom"

View File

@@ -98,6 +98,11 @@
} }
} }
/* Obscure text for password fields */
.cm-wrapper.cm-obscure-text .cm-line {
-webkit-text-security: disc;
}
.cm-editor .cm-gutterElement { .cm-editor .cm-gutterElement {
@apply flex items-center; @apply flex items-center;
transition: color var(--transition-duration); transition: color var(--transition-duration);

View File

@@ -5,7 +5,7 @@ import { keymap, placeholder as placeholderExt, tooltips } from '@codemirror/vie
import classnames from 'classnames'; import classnames from 'classnames';
import { EditorView } from 'codemirror'; import { EditorView } from 'codemirror';
import type { MutableRefObject } from 'react'; import type { MutableRefObject } from 'react';
import { useEffect, useMemo, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { IconButton } from '../IconButton'; import { IconButton } from '../IconButton';
import './Editor.css'; import './Editor.css';
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions'; import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
@@ -15,6 +15,7 @@ import { singleLineExt } from './singleLine';
export interface _EditorProps { export interface _EditorProps {
id?: string; id?: string;
readOnly?: boolean; readOnly?: boolean;
type?: 'text' | 'password';
className?: string; className?: string;
heightMode?: 'auto' | 'full'; heightMode?: 'auto' | 'full';
contentType?: string; contentType?: string;
@@ -33,6 +34,7 @@ export interface _EditorProps {
export function _Editor({ export function _Editor({
readOnly, readOnly,
type = 'text',
heightMode, heightMode,
contentType, contentType,
autoFocus, autoFocus,
@@ -117,21 +119,18 @@ export function _Editor({
}; };
}, [wrapperRef.current, languageExtension]); }, [wrapperRef.current, languageExtension]);
const cmContainer = useMemo( const cmContainer = (
() => ( <div
<div ref={wrapperRef}
ref={wrapperRef} className={classnames(
dangerouslySetInnerHTML={{ __html: '' }} className,
className={classnames( 'cm-wrapper text-base bg-gray-50',
className, type === 'password' && 'cm-obscure-text',
'cm-wrapper text-base bg-gray-50', heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height',
heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height', singleLine ? 'cm-singleline' : 'cm-multiline',
singleLine ? 'cm-singleline' : 'cm-multiline', readOnly && 'cm-readonly',
readOnly && 'cm-readonly', )}
)} />
/>
),
[],
); );
if (singleLine) { if (singleLine) {

View File

@@ -3,8 +3,8 @@ import { LRLanguage } from '@codemirror/language';
import { parseMixed } from '@lezer/common'; import { parseMixed } from '@lezer/common';
import type { GenericCompletionConfig } from '../genericCompletion'; import type { GenericCompletionConfig } from '../genericCompletion';
import { genericCompletion } from '../genericCompletion'; import { genericCompletion } from '../genericCompletion';
import { placeholders } from '../placeholder';
import { textLanguageName } from '../text/extension'; import { textLanguageName } from '../text/extension';
import { placeholders } from '../widgets';
import { completions } from './completion'; import { completions } from './completion';
import { parser as twigParser } from './twig'; import { parser as twigParser } from './twig';

View File

@@ -13,6 +13,7 @@ import {
DotsHorizontalIcon, DotsHorizontalIcon,
DotsVerticalIcon, DotsVerticalIcon,
DragHandleDots2Icon, DragHandleDots2Icon,
EyeClosedIcon,
EyeOpenIcon, EyeOpenIcon,
GearIcon, GearIcon,
HamburgerMenuIcon, HamburgerMenuIcon,
@@ -33,10 +34,10 @@ import {
TriangleRightIcon, TriangleRightIcon,
UpdateIcon, UpdateIcon,
} from '@radix-ui/react-icons'; } from '@radix-ui/react-icons';
import { ReactComponent as LeftPanelHiddenIcon } from '../../assets/icons/LeftPanelHiddenIcon.svg';
import { ReactComponent as LeftPanelVisibleIcon } from '../../assets/icons/LeftPanelVisibleIcon.svg';
import classnames from 'classnames'; import classnames from 'classnames';
import { memo } from 'react'; import { memo } from 'react';
import { ReactComponent as LeftPanelHiddenIcon } from '../../assets/icons/LeftPanelHiddenIcon.svg';
import { ReactComponent as LeftPanelVisibleIcon } from '../../assets/icons/LeftPanelVisibleIcon.svg';
const icons = { const icons = {
archive: ArchiveIcon, archive: ArchiveIcon,
@@ -53,6 +54,7 @@ const icons = {
dotsV: DotsVerticalIcon, dotsV: DotsVerticalIcon,
drag: DragHandleDots2Icon, drag: DragHandleDots2Icon,
eye: EyeOpenIcon, eye: EyeOpenIcon,
eyeClosed: EyeClosedIcon,
gear: GearIcon, gear: GearIcon,
hamburger: HamburgerMenuIcon, hamburger: HamburgerMenuIcon,
home: HomeIcon, home: HomeIcon,

View File

@@ -51,6 +51,7 @@ const _IconButton = forwardRef<HTMLButtonElement, Props>(function IconButton(
'!px-0', '!px-0',
size === 'md' && 'w-9', size === 'md' && 'w-9',
size === 'sm' && 'w-8', size === 'sm' && 'w-8',
size === 'xs' && 'w-7',
)} )}
size={size} size={size}
{...props} {...props}

View File

@@ -3,11 +3,13 @@ import type { HTMLAttributes, ReactNode } from 'react';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import type { EditorProps } from './Editor'; import type { EditorProps } from './Editor';
import { Editor } from './Editor'; import { Editor } from './Editor';
import { IconButton } from './IconButton';
import { HStack, VStack } from './Stacks'; import { HStack, VStack } from './Stacks';
export type InputProps = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> & export type InputProps = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'onFocus'> &
Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'> & { Pick<EditorProps, 'contentType' | 'useTemplating' | 'autocomplete'> & {
name: string; name: string;
type?: 'text' | 'password';
label: string; label: string;
hideLabel?: boolean; hideLabel?: boolean;
labelClassName?: string; labelClassName?: string;
@@ -27,6 +29,7 @@ export type InputProps = Omit<HTMLAttributes<HTMLInputElement>, 'onChange' | 'on
export function Input({ export function Input({
label, label,
type = 'text',
hideLabel, hideLabel,
className, className,
containerClassName, containerClassName,
@@ -42,6 +45,7 @@ export function Input({
require, require,
...props ...props
}: InputProps) { }: InputProps) {
const [obscured, setObscured] = useState(type === 'password');
const [currentValue, setCurrentValue] = useState(defaultValue ?? ''); const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
const id = `input-${name}`; const id = `input-${name}`;
const inputClassName = classnames( const inputClassName = classnames(
@@ -90,12 +94,23 @@ export function Input({
<Editor <Editor
id={id} id={id}
singleLine singleLine
type={type === 'password' && !obscured ? 'text' : type}
defaultValue={defaultValue} defaultValue={defaultValue}
placeholder={placeholder} placeholder={placeholder}
onChange={handleChange} onChange={handleChange}
className={inputClassName} className={inputClassName}
{...props} {...props}
/> />
{type === 'password' && (
<IconButton
title={obscured ? `Show ${label}` : `Obscure ${label}`}
size="xs"
className="mr-0.5"
iconSize="sm"
icon={obscured ? 'eyeClosed' : 'eye'}
onClick={() => setObscured((o) => !o)}
/>
)}
{rightSlot} {rightSlot}
</HStack> </HStack>
</VStack> </VStack>

View File

@@ -101,7 +101,10 @@ export function Tabs({
{option && 'shortLabel' in option {option && 'shortLabel' in option
? option.shortLabel ? option.shortLabel
: option?.label ?? 'Unknown'} : option?.label ?? 'Unknown'}
<Icon icon="triangleDown" className="-mr-1.5" /> <Icon
icon="triangleDown"
className={classnames('-mr-1.5', isActive ? 'opacity-100' : 'opacity-20')}
/>
</Button> </Button>
</RadioDropdown> </RadioDropdown>
); );