mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-07-04 20:11:48 +02:00
Setting to colorize HTTP methods
https://feedback.yaak.app/p/support-colors-for-http-method-in-sidebar
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE settings
|
||||||
|
ADD COLUMN colored_methods BOOLEAN DEFAULT FALSE;
|
||||||
@@ -62,7 +62,7 @@ export type ProxySetting = { "type": "enabled", disabled: boolean, http: string,
|
|||||||
|
|
||||||
export type ProxySettingAuth = { user: string, password: string, };
|
export type ProxySettingAuth = { user: string, password: string, };
|
||||||
|
|
||||||
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, hideWindowControls: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, };
|
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, editorFontSize: number, editorSoftWrap: boolean, hideWindowControls: boolean, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, editorKeymap: EditorKeymap, coloredMethods: boolean, };
|
||||||
|
|
||||||
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
|
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { Environment } from "./gen_models";
|
import type { Environment } from "./gen_models.js";
|
||||||
import type { Folder } from "./gen_models";
|
import type { Folder } from "./gen_models.js";
|
||||||
import type { GrpcRequest } from "./gen_models";
|
import type { GrpcRequest } from "./gen_models.js";
|
||||||
import type { HttpRequest } from "./gen_models";
|
import type { HttpRequest } from "./gen_models.js";
|
||||||
import type { WebsocketRequest } from "./gen_models";
|
import type { WebsocketRequest } from "./gen_models.js";
|
||||||
import type { Workspace } from "./gen_models";
|
import type { Workspace } from "./gen_models.js";
|
||||||
|
|
||||||
export type BatchUpsertResult = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, websocketRequests: Array<WebsocketRequest>, };
|
export type BatchUpsertResult = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, websocketRequests: Array<WebsocketRequest>, };
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ pub struct Settings {
|
|||||||
pub theme_light: String,
|
pub theme_light: String,
|
||||||
pub update_channel: String,
|
pub update_channel: String,
|
||||||
pub editor_keymap: EditorKeymap,
|
pub editor_keymap: EditorKeymap,
|
||||||
|
pub colored_methods: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpsertModelInfo for Settings {
|
impl UpsertModelInfo for Settings {
|
||||||
@@ -160,6 +161,7 @@ impl UpsertModelInfo for Settings {
|
|||||||
(ThemeDark, self.theme_dark.as_str().into()),
|
(ThemeDark, self.theme_dark.as_str().into()),
|
||||||
(ThemeLight, self.theme_light.as_str().into()),
|
(ThemeLight, self.theme_light.as_str().into()),
|
||||||
(UpdateChannel, self.update_channel.into()),
|
(UpdateChannel, self.update_channel.into()),
|
||||||
|
(ColoredMethods, self.colored_methods.into()),
|
||||||
(Proxy, proxy.into()),
|
(Proxy, proxy.into()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -179,6 +181,7 @@ impl UpsertModelInfo for Settings {
|
|||||||
SettingsIden::ThemeDark,
|
SettingsIden::ThemeDark,
|
||||||
SettingsIden::ThemeLight,
|
SettingsIden::ThemeLight,
|
||||||
SettingsIden::UpdateChannel,
|
SettingsIden::UpdateChannel,
|
||||||
|
SettingsIden::ColoredMethods,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +208,7 @@ impl UpsertModelInfo for Settings {
|
|||||||
theme_light: row.get("theme_light")?,
|
theme_light: row.get("theme_light")?,
|
||||||
hide_window_controls: row.get("hide_window_controls")?,
|
hide_window_controls: row.get("hide_window_controls")?,
|
||||||
update_channel: row.get("update_channel")?,
|
update_channel: row.get("update_channel")?,
|
||||||
|
colored_methods: row.get("colored_methods")?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ impl<'a> DbContext<'a> {
|
|||||||
theme_dark: "yaak-dark".to_string(),
|
theme_dark: "yaak-dark".to_string(),
|
||||||
theme_light: "yaak-light".to_string(),
|
theme_light: "yaak-light".to_string(),
|
||||||
update_channel: "stable".to_string(),
|
update_channel: "stable".to_string(),
|
||||||
|
colored_methods: false,
|
||||||
};
|
};
|
||||||
self.upsert(&settings, &UpdateSource::Background).expect("Failed to upsert settings")
|
self.upsert(&settings, &UpdateSource::Background).expect("Failed to upsert settings")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
return workspaces;
|
return workspaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
const r = [...workspaces].sort((a, b) => {
|
return [...workspaces].sort((a, b) => {
|
||||||
const aRecentIndex = recentWorkspaces?.indexOf(a.id);
|
const aRecentIndex = recentWorkspaces?.indexOf(a.id);
|
||||||
const bRecentIndex = recentWorkspaces?.indexOf(b.id);
|
const bRecentIndex = recentWorkspaces?.indexOf(b.id);
|
||||||
|
|
||||||
@@ -250,7 +250,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
return a.createdAt.localeCompare(b.createdAt);
|
return a.createdAt.localeCompare(b.createdAt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return r;
|
|
||||||
}, [recentWorkspaces, workspaces]);
|
}, [recentWorkspaces, workspaces]);
|
||||||
|
|
||||||
const groups = useMemo<CommandPaletteGroup[]>(() => {
|
const groups = useMemo<CommandPaletteGroup[]>(() => {
|
||||||
@@ -272,7 +271,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
searchText: resolvedModelNameWithFolders(r),
|
searchText: resolvedModelNameWithFolders(r),
|
||||||
label: (
|
label: (
|
||||||
<HStack space={2}>
|
<HStack space={2}>
|
||||||
<HttpMethodTag className="text-text-subtlest" request={r} />
|
<HttpMethodTag short className="text-xs" request={r} />
|
||||||
<div className="truncate">{resolvedModelNameWithFolders(r)}</div>
|
<div className="truncate">{resolvedModelNameWithFolders(r)}</div>
|
||||||
</HStack>
|
</HStack>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -41,8 +41,7 @@ import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
|||||||
import { InlineCode } from './core/InlineCode';
|
import { InlineCode } from './core/InlineCode';
|
||||||
import type { Pair } from './core/PairEditor';
|
import type { Pair } from './core/PairEditor';
|
||||||
import { PlainInput } from './core/PlainInput';
|
import { PlainInput } from './core/PlainInput';
|
||||||
import type { TabItem } from './core/Tabs/Tabs';
|
import { TabContent, TabItem, Tabs } from './core/Tabs/Tabs';
|
||||||
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
|
||||||
import { EmptyStateText } from './EmptyStateText';
|
import { EmptyStateText } from './EmptyStateText';
|
||||||
import { FormMultipartEditor } from './FormMultipartEditor';
|
import { FormMultipartEditor } from './FormMultipartEditor';
|
||||||
import { FormUrlencodedEditor } from './FormUrlencodedEditor';
|
import { FormUrlencodedEditor } from './FormUrlencodedEditor';
|
||||||
@@ -50,6 +49,7 @@ import { GraphQLEditor } from './GraphQLEditor';
|
|||||||
import { HeadersEditor } from './HeadersEditor';
|
import { HeadersEditor } from './HeadersEditor';
|
||||||
import { HttpAuthenticationEditor } from './HttpAuthenticationEditor';
|
import { HttpAuthenticationEditor } from './HttpAuthenticationEditor';
|
||||||
import { MarkdownEditor } from './MarkdownEditor';
|
import { MarkdownEditor } from './MarkdownEditor';
|
||||||
|
import { RequestMethodDropdown } from './RequestMethodDropdown';
|
||||||
import { UrlBar } from './UrlBar';
|
import { UrlBar } from './UrlBar';
|
||||||
import { UrlParametersEditor } from './UrlParameterEditor';
|
import { UrlParametersEditor } from './UrlParameterEditor';
|
||||||
|
|
||||||
@@ -138,10 +138,9 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
|
|||||||
activeRequest.bodyType === BODY_TYPE_FORM_URLENCODED ||
|
activeRequest.bodyType === BODY_TYPE_FORM_URLENCODED ||
|
||||||
activeRequest.bodyType === BODY_TYPE_FORM_MULTIPART
|
activeRequest.bodyType === BODY_TYPE_FORM_MULTIPART
|
||||||
) {
|
) {
|
||||||
const n = Array.isArray(activeRequest.body?.form)
|
numParams = Array.isArray(activeRequest.body?.form)
|
||||||
? activeRequest.body.form.filter((p) => p.name).length
|
? activeRequest.body.form.filter((p) => p.name).length
|
||||||
: 0;
|
: 0;
|
||||||
numParams = n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const tabs = useMemo<TabItem[]>(
|
const tabs = useMemo<TabItem[]>(
|
||||||
@@ -314,11 +313,6 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
|
|||||||
[activeRequest.id, sendRequest],
|
[activeRequest.id, sendRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMethodChange = useCallback(
|
|
||||||
(method: string) => patchModel(activeRequest, { method }),
|
|
||||||
[activeRequest],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleUrlChange = useCallback(
|
const handleUrlChange = useCallback(
|
||||||
(url: string) => patchModel(activeRequest, { url }),
|
(url: string) => patchModel(activeRequest, { url }),
|
||||||
[activeRequest],
|
[activeRequest],
|
||||||
@@ -335,14 +329,17 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
|
|||||||
stateKey={`url.${activeRequest.id}`}
|
stateKey={`url.${activeRequest.id}`}
|
||||||
key={forceUpdateKey + urlKey}
|
key={forceUpdateKey + urlKey}
|
||||||
url={activeRequest.url}
|
url={activeRequest.url}
|
||||||
method={activeRequest.method}
|
|
||||||
placeholder="https://example.com"
|
placeholder="https://example.com"
|
||||||
onPasteOverwrite={handlePaste}
|
onPasteOverwrite={handlePaste}
|
||||||
autocomplete={autocomplete}
|
autocomplete={autocomplete}
|
||||||
onSend={handleSend}
|
onSend={handleSend}
|
||||||
onCancel={cancelResponse}
|
onCancel={cancelResponse}
|
||||||
onMethodChange={handleMethodChange}
|
|
||||||
onUrlChange={handleUrlChange}
|
onUrlChange={handleUrlChange}
|
||||||
|
leftSlot={
|
||||||
|
<div className="py-0.5">
|
||||||
|
<RequestMethodDropdown request={activeRequest} className="ml-0.5 !h-full" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
forceUpdateKey={updateKey}
|
forceUpdateKey={updateKey}
|
||||||
isLoading={activeResponse != null && activeResponse.state !== 'closed'}
|
isLoading={activeResponse != null && activeResponse.state !== 'closed'}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ export const RecentHttpResponsesDropdown = function ResponsePane({
|
|||||||
...responses.map((r: HttpResponse) => ({
|
...responses.map((r: HttpResponse) => ({
|
||||||
label: (
|
label: (
|
||||||
<HStack space={2}>
|
<HStack space={2}>
|
||||||
<HttpStatusTag className="text-sm" response={r} />
|
<HttpStatusTag short className="text-xs" response={r} />
|
||||||
<span className="text-text-subtle">→</span>{' '}
|
<span className="text-text-subtle">→</span>{' '}
|
||||||
<span className="font-mono text-sm">{r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'}</span>
|
<span className="font-mono text-sm">{r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'}</span>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ export function RecentRequestsDropdown({ className }: Props) {
|
|||||||
|
|
||||||
recentRequestItems.push({
|
recentRequestItems.push({
|
||||||
label: resolvedModelName(request),
|
label: resolvedModelName(request),
|
||||||
leftSlot: <HttpMethodTag request={request} />,
|
leftSlot: <HttpMethodTag short className="text-xs" request={request} />,
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
await router.navigate({
|
await router.navigate({
|
||||||
to: '/workspaces/$workspaceId',
|
to: '/workspaces/$workspaceId',
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
|
import { HttpRequest, patchModel } from '@yaakapp-internal/models';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { memo, useMemo } from 'react';
|
import { memo, useCallback, useMemo } from 'react';
|
||||||
import { showPrompt } from '../lib/prompt';
|
import { showPrompt } from '../lib/prompt';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import type { DropdownItem } from './core/Dropdown';
|
import type { DropdownItem } from './core/Dropdown';
|
||||||
|
import { HttpMethodTag } from './core/HttpMethodTag';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
import type { RadioDropdownItem } from './core/RadioDropdown';
|
import type { RadioDropdownItem } from './core/RadioDropdown';
|
||||||
import { RadioDropdown } from './core/RadioDropdown';
|
import { RadioDropdown } from './core/RadioDropdown';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
method: string;
|
request: HttpRequest;
|
||||||
className?: string;
|
className?: string;
|
||||||
onChange: (method: string) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const radioItems: RadioDropdownItem<string>[] = [
|
const radioItems: RadioDropdownItem<string>[] = [
|
||||||
@@ -28,10 +29,13 @@ const radioItems: RadioDropdownItem<string>[] = [
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
||||||
method,
|
request,
|
||||||
onChange,
|
|
||||||
className,
|
className,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const handleChange = useCallback(async (method: string) => {
|
||||||
|
await patchModel(request, { method });
|
||||||
|
}, []);
|
||||||
|
|
||||||
const itemsAfter = useMemo<DropdownItem[]>(
|
const itemsAfter = useMemo<DropdownItem[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -49,17 +53,22 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({
|
|||||||
placeholder: 'CUSTOM',
|
placeholder: 'CUSTOM',
|
||||||
});
|
});
|
||||||
if (newMethod == null) return;
|
if (newMethod == null) return;
|
||||||
onChange(newMethod);
|
await handleChange(newMethod);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[onChange],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RadioDropdown value={method} items={radioItems} itemsAfter={itemsAfter} onChange={onChange}>
|
<RadioDropdown
|
||||||
|
value={request.method}
|
||||||
|
items={radioItems}
|
||||||
|
itemsAfter={itemsAfter}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
<Button size="xs" className={classNames(className, 'text-text-subtle hover:text')}>
|
<Button size="xs" className={classNames(className, 'text-text-subtle hover:text')}>
|
||||||
{method.toUpperCase()}
|
<HttpMethodTag request={request}/>
|
||||||
</Button>
|
</Button>
|
||||||
</RadioDropdown>
|
</RadioDropdown>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -122,6 +122,11 @@ export function SettingsAppearance() {
|
|||||||
title="Wrap Editor Lines"
|
title="Wrap Editor Lines"
|
||||||
onChange={(editorSoftWrap) => patchModel(settings, { editorSoftWrap })}
|
onChange={(editorSoftWrap) => patchModel(settings, { editorSoftWrap })}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
checked={settings.coloredMethods}
|
||||||
|
title="Colorize Request Methods"
|
||||||
|
onChange={(coloredMethods) => patchModel(settings, { coloredMethods })}
|
||||||
|
/>
|
||||||
|
|
||||||
{type() !== 'macos' && (
|
{type() !== 'macos' && (
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -9,11 +9,9 @@ import { IconButton } from './core/IconButton';
|
|||||||
import type { InputProps } from './core/Input';
|
import type { InputProps } from './core/Input';
|
||||||
import { Input } from './core/Input';
|
import { Input } from './core/Input';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack } from './core/Stacks';
|
||||||
import { RequestMethodDropdown } from './RequestMethodDropdown';
|
|
||||||
|
|
||||||
type Props = Pick<HttpRequest, 'url'> & {
|
type Props = Pick<HttpRequest, 'url'> & {
|
||||||
className?: string;
|
className?: string;
|
||||||
method: HttpRequest['method'] | null;
|
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
onSend: () => void;
|
onSend: () => void;
|
||||||
onUrlChange: (url: string) => void;
|
onUrlChange: (url: string) => void;
|
||||||
@@ -21,10 +19,10 @@ type Props = Pick<HttpRequest, 'url'> & {
|
|||||||
onPasteOverwrite?: InputProps['onPasteOverwrite'];
|
onPasteOverwrite?: InputProps['onPasteOverwrite'];
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
submitIcon?: IconProps['icon'] | null;
|
submitIcon?: IconProps['icon'] | null;
|
||||||
onMethodChange?: (method: string) => void;
|
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
forceUpdateKey: string;
|
forceUpdateKey: string;
|
||||||
rightSlot?: ReactNode;
|
rightSlot?: ReactNode;
|
||||||
|
leftSlot?: ReactNode;
|
||||||
autocomplete?: InputProps['autocomplete'];
|
autocomplete?: InputProps['autocomplete'];
|
||||||
stateKey: InputProps['stateKey'];
|
stateKey: InputProps['stateKey'];
|
||||||
};
|
};
|
||||||
@@ -33,16 +31,15 @@ export const UrlBar = memo(function UrlBar({
|
|||||||
forceUpdateKey,
|
forceUpdateKey,
|
||||||
onUrlChange,
|
onUrlChange,
|
||||||
url,
|
url,
|
||||||
method,
|
|
||||||
placeholder,
|
placeholder,
|
||||||
className,
|
className,
|
||||||
onSend,
|
onSend,
|
||||||
onCancel,
|
onCancel,
|
||||||
onMethodChange,
|
|
||||||
onPaste,
|
onPaste,
|
||||||
onPasteOverwrite,
|
onPasteOverwrite,
|
||||||
submitIcon = 'send_horizontal',
|
submitIcon = 'send_horizontal',
|
||||||
autocomplete,
|
autocomplete,
|
||||||
|
leftSlot,
|
||||||
rightSlot,
|
rightSlot,
|
||||||
isLoading,
|
isLoading,
|
||||||
stateKey,
|
stateKey,
|
||||||
@@ -87,18 +84,7 @@ export const UrlBar = memo(function UrlBar({
|
|||||||
onChange={onUrlChange}
|
onChange={onUrlChange}
|
||||||
defaultValue={url}
|
defaultValue={url}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
leftSlot={
|
leftSlot={leftSlot}
|
||||||
method != null &&
|
|
||||||
onMethodChange != null && (
|
|
||||||
<div className="py-0.5">
|
|
||||||
<RequestMethodDropdown
|
|
||||||
method={method}
|
|
||||||
onChange={onMethodChange}
|
|
||||||
className="ml-0.5 !h-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
rightSlot={
|
rightSlot={
|
||||||
<HStack space={0.5}>
|
<HStack space={0.5}>
|
||||||
{rightSlot && <div className="py-0.5 h-full">{rightSlot}</div>}
|
{rightSlot && <div className="py-0.5 h-full">{rightSlot}</div>}
|
||||||
|
|||||||
@@ -642,7 +642,7 @@ function MenuItem({ className, focused, onFocus, item, onSelect, ...props }: Men
|
|||||||
justify="start"
|
justify="start"
|
||||||
leftSlot={
|
leftSlot={
|
||||||
(isLoading || item.leftSlot) && (
|
(isLoading || item.leftSlot) && (
|
||||||
<div className={classNames('pr-2 flex justify-start opacity-70')}>
|
<div className={classNames('pr-2 flex justify-start [&_svg]:opacity-70')}>
|
||||||
{isLoading ? <LoadingIcon /> : item.leftSlot}
|
{isLoading ? <LoadingIcon /> : item.leftSlot}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
import { GrpcRequest, HttpRequest, settingsAtom, WebsocketRequest } from '@yaakapp-internal/models';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useAtomValue } from 'jotai';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
request: HttpRequest | GrpcRequest | WebsocketRequest;
|
request: HttpRequest | GrpcRequest | WebsocketRequest;
|
||||||
className?: string;
|
className?: string;
|
||||||
shortNames?: boolean;
|
short?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const methodNames: Record<string, string> = {
|
const methodNames: Record<string, string> = {
|
||||||
@@ -18,7 +19,8 @@ const methodNames: Record<string, string> = {
|
|||||||
query: 'QURY',
|
query: 'QURY',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function HttpMethodTag({ request, className }: Props) {
|
export function HttpMethodTag({ request, className, short }: Props) {
|
||||||
|
const settings = useAtomValue(settingsAtom);
|
||||||
const method =
|
const method =
|
||||||
request.model === 'http_request' && request.bodyType === 'graphql'
|
request.model === 'http_request' && request.bodyType === 'graphql'
|
||||||
? 'GQL'
|
? 'GQL'
|
||||||
@@ -26,19 +28,34 @@ export function HttpMethodTag({ request, className }: Props) {
|
|||||||
? 'GRPC'
|
? 'GRPC'
|
||||||
: request.model === 'websocket_request'
|
: request.model === 'websocket_request'
|
||||||
? 'WS'
|
? 'WS'
|
||||||
: (methodNames[request.method.toLowerCase()] ?? request.method.slice(0, 4));
|
: request.method;
|
||||||
|
let label = method.toUpperCase();
|
||||||
|
|
||||||
const paddedMethod = method.padStart(4, ' ').toUpperCase();
|
if (short) {
|
||||||
|
label = methodNames[method.toLowerCase()] ?? method.slice(0, 4);
|
||||||
|
label = label.padStart(4, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'text-xs font-mono text-text-subtle flex-shrink-0 whitespace-pre',
|
!settings.coloredMethods && 'text-text-subtle',
|
||||||
|
settings.coloredMethods && method === 'GQL' && 'text-info',
|
||||||
|
settings.coloredMethods && method === 'WS' && 'text-info',
|
||||||
|
settings.coloredMethods && method === 'GRPC' && 'text-info',
|
||||||
|
settings.coloredMethods && method === 'OPTIONS' && 'text-info',
|
||||||
|
settings.coloredMethods && method === 'HEAD' && 'text-info',
|
||||||
|
settings.coloredMethods && method === 'GET' && 'text-primary',
|
||||||
|
settings.coloredMethods && method === 'PUT' && 'text-warning',
|
||||||
|
settings.coloredMethods && method === 'PATCH' && 'text-notice',
|
||||||
|
settings.coloredMethods && method === 'POST' && 'text-success',
|
||||||
|
settings.coloredMethods && method === 'DELETE' && 'text-danger',
|
||||||
|
'font-mono flex-shrink-0 whitespace-pre',
|
||||||
'pt-[0.25em]', // Fix for monospace font not vertically centering
|
'pt-[0.25em]', // Fix for monospace font not vertically centering
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{paddedMethod}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,20 @@ interface Props {
|
|||||||
response: HttpResponse;
|
response: HttpResponse;
|
||||||
className?: string;
|
className?: string;
|
||||||
showReason?: boolean;
|
showReason?: boolean;
|
||||||
|
short?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HttpStatusTag({ response, className, showReason }: Props) {
|
export function HttpStatusTag({ response, className, showReason, short }: Props) {
|
||||||
const { status, state } = response;
|
const { status, state } = response;
|
||||||
|
|
||||||
let colorClass;
|
let colorClass;
|
||||||
let label = `${status}`;
|
let label = `${status}`;
|
||||||
|
|
||||||
if (state === 'initialized') {
|
if (state === 'initialized') {
|
||||||
label = 'CONNECTING';
|
label = short ? 'CONN' : 'CONNECTING';
|
||||||
colorClass = 'text-text-subtle';
|
colorClass = 'text-text-subtle';
|
||||||
} else if (status < 100) {
|
} else if (status < 100) {
|
||||||
label = 'ERROR';
|
label = short ? 'ERR' : 'ERROR';
|
||||||
colorClass = 'text-danger';
|
colorClass = 'text-danger';
|
||||||
} else if (status < 200) {
|
} else if (status < 200) {
|
||||||
colorClass = 'text-info';
|
colorClass = 'text-info';
|
||||||
@@ -33,8 +34,7 @@ export function HttpStatusTag({ response, className, showReason }: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={classNames(className, 'font-mono', colorClass)}>
|
<span className={classNames(className, 'font-mono', colorClass)}>
|
||||||
{label}{' '}
|
{label} {showReason && 'statusReason' in response ? response.statusReason : null}
|
||||||
{showReason && 'statusReason' in response ? response.statusReason : null}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,11 +53,11 @@ export type InputProps = Pick<
|
|||||||
> & {
|
> & {
|
||||||
className?: string;
|
className?: string;
|
||||||
containerClassName?: string;
|
containerClassName?: string;
|
||||||
|
inputWrapperClassName?: string;
|
||||||
defaultValue?: string | null;
|
defaultValue?: string | null;
|
||||||
disableObscureToggle?: boolean;
|
disableObscureToggle?: boolean;
|
||||||
fullHeight?: boolean;
|
fullHeight?: boolean;
|
||||||
hideLabel?: boolean;
|
hideLabel?: boolean;
|
||||||
inputWrapperClassName?: string;
|
|
||||||
help?: ReactNode;
|
help?: ReactNode;
|
||||||
label: ReactNode;
|
label: ReactNode;
|
||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from '
|
|||||||
import type { XYCoord } from 'react-dnd';
|
import type { XYCoord } from 'react-dnd';
|
||||||
import { useDrag, useDrop } from 'react-dnd';
|
import { useDrag, useDrop } from 'react-dnd';
|
||||||
import { activeRequestAtom } from '../../hooks/useActiveRequest';
|
import { activeRequestAtom } from '../../hooks/useActiveRequest';
|
||||||
import {allRequestsAtom} from "../../hooks/useAllRequests";
|
import { allRequestsAtom } from '../../hooks/useAllRequests';
|
||||||
import { useScrollIntoView } from '../../hooks/useScrollIntoView';
|
import { useScrollIntoView } from '../../hooks/useScrollIntoView';
|
||||||
import { useSidebarItemCollapsed } from '../../hooks/useSidebarItemCollapsed';
|
import { useSidebarItemCollapsed } from '../../hooks/useSidebarItemCollapsed';
|
||||||
import { jotaiStore } from '../../lib/jotai';
|
import { jotaiStore } from '../../lib/jotai';
|
||||||
@@ -214,10 +214,13 @@ export const SidebarItem = memo(function SidebarItem({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const opacitySubtle = 'opacity-80';
|
||||||
|
|
||||||
const itemPrefix = item.model !== 'folder' && (
|
const itemPrefix = item.model !== 'folder' && (
|
||||||
<HttpMethodTag
|
<HttpMethodTag
|
||||||
|
short
|
||||||
request={item}
|
request={item}
|
||||||
className={classNames(!(active || selected) && 'text-text-subtlest')}
|
className={classNames('text-xs', !(active || selected) && opacitySubtle)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -287,7 +290,11 @@ export const SidebarItem = memo(function SidebarItem({
|
|||||||
{latestHttpResponse.state !== 'closed' ? (
|
{latestHttpResponse.state !== 'closed' ? (
|
||||||
<LoadingIcon size="sm" className="text-text-subtlest" />
|
<LoadingIcon size="sm" className="text-text-subtlest" />
|
||||||
) : (
|
) : (
|
||||||
<HttpStatusTag className="text-xs" response={latestHttpResponse} />
|
<HttpStatusTag
|
||||||
|
short
|
||||||
|
className={classNames('text-xs', !(active || selected) && opacitySubtle)}
|
||||||
|
response={latestHttpResponse}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
Reference in New Issue
Block a user