mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-17 23:14:03 +01:00
snake_case icons and better toast styles
This commit is contained in:
@@ -46,7 +46,7 @@
|
||||
"@tauri-apps/plugin-log": "^2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-os": "^2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-shell": "^2.0.0-rc.1",
|
||||
"@yaakapp/api": "^0.2.3",
|
||||
"@yaakapp/api": "^0.2.4",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.5.1",
|
||||
"cm6-graphql": "^0.0.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.5",
|
||||
"main": "lib/index.js",
|
||||
"typings": "./lib/index.d.ts",
|
||||
"files": [
|
||||
|
||||
@@ -26,6 +26,8 @@ export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunc
|
||||
|
||||
export type CallTemplateFunctionResponse = { value: string | null, };
|
||||
|
||||
export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
||||
|
||||
export type CopyTextRequest = { text: string, };
|
||||
|
||||
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
@@ -52,6 +54,8 @@ export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>,
|
||||
|
||||
export type HttpRequestAction = { key: string, label: string, icon: string | null, };
|
||||
|
||||
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle";
|
||||
|
||||
export type ImportRequest = { content: string, };
|
||||
|
||||
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
||||
@@ -74,7 +78,7 @@ export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||
|
||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||
|
||||
export type ShowToastRequest = { message: string, variant: ToastVariant, };
|
||||
export type ShowToastRequest = { message: string, color?: Color | null, icon?: Icon | null, };
|
||||
|
||||
export type TemplateFunction = { name: string, args: Array<TemplateFunctionArg>, };
|
||||
|
||||
@@ -91,5 +95,3 @@ export type TemplateFunctionSelectArg = { options: Array<TemplateFunctionSelectO
|
||||
export type TemplateFunctionSelectOption = { name: string, value: string, };
|
||||
|
||||
export type TemplateFunctionTextArg = { placeholder?: string | null, name: string, optional?: boolean | null, label?: string | null, defaultValue?: string | null, };
|
||||
|
||||
export type ToastVariant = "custom" | "copied" | "success" | "info" | "warning" | "error";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type * from './plugins';
|
||||
export type * from './themes';
|
||||
|
||||
export * from './gen/models';
|
||||
export * from './gen/model_util';
|
||||
export * from './gen/events';
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { FindHttpResponsesRequest } from '../gen/FindHttpResponsesRequest';
|
||||
import { FindHttpResponsesResponse } from '../gen/FindHttpResponsesResponse';
|
||||
import { GetHttpRequestByIdRequest } from '../gen/GetHttpRequestByIdRequest';
|
||||
import { GetHttpRequestByIdResponse } from '../gen/GetHttpRequestByIdResponse';
|
||||
import { RenderHttpRequestRequest } from '../gen/RenderHttpRequestRequest';
|
||||
import { RenderHttpRequestResponse } from '../gen/RenderHttpRequestResponse';
|
||||
import { SendHttpRequestRequest } from '../gen/SendHttpRequestRequest';
|
||||
import { SendHttpRequestResponse } from '../gen/SendHttpRequestResponse';
|
||||
import { ShowToastRequest } from '../gen/ShowToastRequest';
|
||||
import {
|
||||
FindHttpResponsesRequest,
|
||||
FindHttpResponsesResponse,
|
||||
GetHttpRequestByIdRequest,
|
||||
GetHttpRequestByIdResponse,
|
||||
RenderHttpRequestRequest,
|
||||
RenderHttpRequestResponse,
|
||||
SendHttpRequestRequest,
|
||||
SendHttpRequestResponse,
|
||||
ShowToastRequest,
|
||||
} from '../gen/events';
|
||||
|
||||
export type Context = {
|
||||
clipboard: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CallHttpRequestActionArgs } from '../gen/CallHttpRequestActionArgs';
|
||||
import { HttpRequestAction } from '../gen/HttpRequestAction';
|
||||
import { CallHttpRequestActionArgs, HttpRequestAction } from '../gen/events';
|
||||
import { Context } from './Context';
|
||||
|
||||
export type HttpRequestActionPlugin = HttpRequestAction & {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
import { Environment } from '../gen/Environment';
|
||||
import { Folder } from '../gen/Folder';
|
||||
import { HttpRequest } from '../gen/HttpRequest';
|
||||
import { Workspace } from '../gen/Workspace';
|
||||
import { Environment, Folder, HttpRequest, Workspace } from '../gen/model_util';
|
||||
import { AtLeast } from '../helpers';
|
||||
import { Context } from './Context';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CallTemplateFunctionArgs } from '../gen/CallTemplateFunctionArgs';
|
||||
import { TemplateFunction } from '../gen/TemplateFunction';
|
||||
import { CallTemplateFunctionArgs, TemplateFunction } from '../gen/events';
|
||||
import { Context } from './Context';
|
||||
|
||||
export type TemplateFunctionPlugin = TemplateFunction & {
|
||||
|
||||
Binary file not shown.
@@ -10,8 +10,8 @@ use std::process::exit;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use base64::Engine;
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use chrono::Utc;
|
||||
use fern::colors::ColoredLevelConfig;
|
||||
use log::{debug, error, info, warn};
|
||||
@@ -59,9 +59,9 @@ use yaak_models::queries::{
|
||||
};
|
||||
use yaak_plugin_runtime::events::{
|
||||
BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
|
||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse,
|
||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon,
|
||||
InternalEvent, InternalEventPayload, RenderHttpRequestResponse, SendHttpRequestResponse,
|
||||
ShowToastRequest, ToastVariant,
|
||||
ShowToastRequest,
|
||||
};
|
||||
use yaak_plugin_runtime::plugin_handle::PluginHandle;
|
||||
use yaak_templates::{Parser, Tokens};
|
||||
@@ -2146,7 +2146,8 @@ async fn handle_plugin_event<R: Runtime>(
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
||||
variant: ToastVariant::Info,
|
||||
icon: Some(Icon::Info),
|
||||
..Default::default()
|
||||
}),
|
||||
None,
|
||||
);
|
||||
@@ -2223,9 +2224,9 @@ fn get_focused_window_no_lock<R: Runtime>(app_handle: &AppHandle<R>) -> Option<W
|
||||
}
|
||||
})
|
||||
.collect::<Vec<WebviewWindow<R>>>();
|
||||
|
||||
|
||||
if main_windows.len() == 1 {
|
||||
return main_windows.iter().next().map(|w| w.clone())
|
||||
return main_windows.iter().next().map(|w| w.clone());
|
||||
}
|
||||
|
||||
main_windows
|
||||
|
||||
@@ -169,27 +169,43 @@ pub struct RenderHttpRequestResponse {
|
||||
#[ts(export, export_to="events.ts")]
|
||||
pub struct ShowToastRequest {
|
||||
pub message: String,
|
||||
pub variant: ToastVariant,
|
||||
#[ts(optional = nullable)]
|
||||
pub color: Option<Color>,
|
||||
#[ts(optional = nullable)]
|
||||
pub icon: Option<Icon>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to="events.ts")]
|
||||
pub enum ToastVariant {
|
||||
pub enum Color {
|
||||
Custom,
|
||||
Copied,
|
||||
Success,
|
||||
Default,
|
||||
Primary,
|
||||
Secondary,
|
||||
Info,
|
||||
Success,
|
||||
Notice,
|
||||
Warning,
|
||||
Error,
|
||||
Danger,
|
||||
}
|
||||
|
||||
impl Default for ToastVariant {
|
||||
impl Default for Color {
|
||||
fn default() -> Self {
|
||||
ToastVariant::Info
|
||||
Color::Default
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(export, export_to="events.ts")]
|
||||
pub enum Icon {
|
||||
Copy,
|
||||
Info,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to="events.ts")]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Cookie } from '@yaakapp/api';
|
||||
import { useCookieJars } from '../hooks/useCookieJars';
|
||||
import { useUpdateCookieJar } from '../hooks/useUpdateCookieJar';
|
||||
import { cookieDomain } from '../lib/models';
|
||||
import { cookieDomain } from '../lib/model_util';
|
||||
import { Banner } from './core/Banner';
|
||||
import { IconButton } from './core/IconButton';
|
||||
import { InlineCode } from './core/InlineCode';
|
||||
|
||||
@@ -67,7 +67,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||
iconSize="md"
|
||||
color="custom"
|
||||
title="Add sub environment"
|
||||
icon="plusCircle"
|
||||
icon="plus_circle"
|
||||
iconClassName="text-text-subtlest group-hover:text-text-subtle"
|
||||
className="group"
|
||||
onClick={handleCreateEnvironment}
|
||||
@@ -177,7 +177,7 @@ const EnvironmentEditor = function ({
|
||||
<IconButton
|
||||
iconClassName="text-text-subtlest"
|
||||
size="sm"
|
||||
icon={valueVisibility.value ? 'eye' : 'eyeClosed'}
|
||||
icon={valueVisibility.value ? 'eye' : 'eye_closed'}
|
||||
title={valueVisibility.value ? 'Hide Values' : 'Reveal Values'}
|
||||
onClick={() => {
|
||||
return valueVisibility.set((v) => !v);
|
||||
|
||||
@@ -30,7 +30,7 @@ import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
|
||||
import { workspacesAtom } from '../hooks/useWorkspaces';
|
||||
import { useZoom } from '../hooks/useZoom';
|
||||
import { extractKeyValue } from '../lib/keyValueStore';
|
||||
import { modelsEq } from '../lib/models';
|
||||
import { modelsEq } from '../lib/model_util';
|
||||
import { catppuccinMacchiato } from '../lib/theme/themes/catppuccin';
|
||||
import { githubLight } from '../lib/theme/themes/github';
|
||||
import { hotdogStandDefault } from '../lib/theme/themes/hotdog-stand';
|
||||
@@ -79,16 +79,16 @@ export function GlobalHooks() {
|
||||
model.model === 'http_response'
|
||||
? httpResponsesQueryKey(model)
|
||||
: model.model === 'folder'
|
||||
? foldersQueryKey(model)
|
||||
: model.model === 'grpc_connection'
|
||||
? grpcConnectionsQueryKey(model)
|
||||
: model.model === 'grpc_event'
|
||||
? grpcEventsQueryKey(model)
|
||||
: model.model === 'key_value'
|
||||
? keyValueQueryKey(model)
|
||||
: model.model === 'cookie_jar'
|
||||
? cookieJarsQueryKey(model)
|
||||
: null;
|
||||
? foldersQueryKey(model)
|
||||
: model.model === 'grpc_connection'
|
||||
? grpcConnectionsQueryKey(model)
|
||||
: model.model === 'grpc_event'
|
||||
? grpcEventsQueryKey(model)
|
||||
: model.model === 'key_value'
|
||||
? keyValueQueryKey(model)
|
||||
: model.model === 'cookie_jar'
|
||||
? cookieJarsQueryKey(model)
|
||||
: null;
|
||||
|
||||
if (model.model === 'http_request' && windowLabel !== getCurrentWebviewWindow().label) {
|
||||
wasUpdatedExternally(model.id);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useGrpcEvents } from '../hooks/useGrpcEvents';
|
||||
import { usePinnedGrpcConnection } from '../hooks/usePinnedGrpcConnection';
|
||||
import { useStateWithDeps } from '../hooks/useStateWithDeps';
|
||||
import type { GrpcEvent, GrpcRequest } from '@yaakapp/api';
|
||||
import { isResponseLoading } from '../lib/models';
|
||||
import { isResponseLoading } from '../lib/model_util';
|
||||
import { Banner } from './core/Banner';
|
||||
import { Button } from './core/Button';
|
||||
import { Icon } from './core/Icon';
|
||||
@@ -197,34 +197,34 @@ function EventRow({
|
||||
eventType === 'server_message'
|
||||
? 'text-info'
|
||||
: eventType === 'client_message'
|
||||
? 'text-primary'
|
||||
: eventType === 'error' || (status != null && status > 0)
|
||||
? 'text-danger'
|
||||
: eventType === 'connection_end'
|
||||
? 'text-success'
|
||||
: 'text-text-subtle'
|
||||
? 'text-primary'
|
||||
: eventType === 'error' || (status != null && status > 0)
|
||||
? 'text-danger'
|
||||
: eventType === 'connection_end'
|
||||
? 'text-success'
|
||||
: 'text-text-subtle'
|
||||
}
|
||||
title={
|
||||
eventType === 'server_message'
|
||||
? 'Server message'
|
||||
: eventType === 'client_message'
|
||||
? 'Client message'
|
||||
: eventType === 'error' || (status != null && status > 0)
|
||||
? 'Error'
|
||||
: eventType === 'connection_end'
|
||||
? 'Connection response'
|
||||
: undefined
|
||||
? 'Client message'
|
||||
: eventType === 'error' || (status != null && status > 0)
|
||||
? 'Error'
|
||||
: eventType === 'connection_end'
|
||||
? 'Connection response'
|
||||
: undefined
|
||||
}
|
||||
icon={
|
||||
eventType === 'server_message'
|
||||
? 'arrowBigDownDash'
|
||||
? 'arrow_big_down_dash'
|
||||
: eventType === 'client_message'
|
||||
? 'arrowBigUpDash'
|
||||
: eventType === 'error' || (status != null && status > 0)
|
||||
? 'alert'
|
||||
: eventType === 'connection_end'
|
||||
? 'check'
|
||||
: 'info'
|
||||
? 'arrow_big_up_dash'
|
||||
: eventType === 'error' || (status != null && status > 0)
|
||||
? 'alert_triangle'
|
||||
: eventType === 'connection_end'
|
||||
? 'check'
|
||||
: 'info'
|
||||
}
|
||||
/>
|
||||
<div className={classNames('w-full truncate text-xs')}>
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { ReflectResponseService } from '../hooks/useGrpc';
|
||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import type { GrpcMetadataEntry, GrpcRequest } from '@yaakapp/api';
|
||||
import { AUTH_TYPE_BASIC, AUTH_TYPE_BEARER, AUTH_TYPE_NONE } from '../lib/models';
|
||||
import { AUTH_TYPE_BASIC, AUTH_TYPE_BEARER, AUTH_TYPE_NONE } from '../lib/model_util';
|
||||
import { BasicAuth } from './BasicAuth';
|
||||
import { BearerAuth } from './BearerAuth';
|
||||
import { Button } from './core/Button';
|
||||
@@ -218,7 +218,7 @@ export function GrpcConnectionSetupPane({
|
||||
<Button
|
||||
size="sm"
|
||||
variant="border"
|
||||
rightSlot={<Icon className="text-text-subtlest" size="sm" icon="chevronDown" />}
|
||||
rightSlot={<Icon className="text-text-subtlest" size="sm" icon="chevron_down" />}
|
||||
disabled={isStreaming || services == null}
|
||||
className={classNames(
|
||||
'font-mono text-editor min-w-[5rem] !ring-0',
|
||||
@@ -254,7 +254,7 @@ export function GrpcConnectionSetupPane({
|
||||
title={isStreaming ? 'Connect' : 'Send'}
|
||||
hotkeyAction="grpc_request.send"
|
||||
onClick={isStreaming ? handleSend : handleConnect}
|
||||
icon={isStreaming ? 'sendHorizontal' : 'arrowUpDown'}
|
||||
icon={isStreaming ? 'send_horizontal' : 'arrow_up_down'}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@@ -269,8 +269,8 @@ export function GrpcConnectionSetupPane({
|
||||
isStreaming
|
||||
? 'x'
|
||||
: methodType.includes('streaming')
|
||||
? 'arrowUpDown'
|
||||
: 'sendHorizontal'
|
||||
? 'arrow_up_down'
|
||||
: 'send_horizontal'
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -42,7 +42,7 @@ export function OpenWorkspaceDialog({ hide, workspace }: Props) {
|
||||
<Button
|
||||
className="focus"
|
||||
color="secondary"
|
||||
rightSlot={<Icon icon="externalLink" />}
|
||||
rightSlot={<Icon icon="external_link" />}
|
||||
onClick={() => {
|
||||
hide();
|
||||
openWorkspace.mutate({ workspaceId: workspace.id, inNewWindow: true });
|
||||
|
||||
@@ -55,7 +55,7 @@ export function RecentConnectionsDropdown({
|
||||
>
|
||||
<IconButton
|
||||
title="Show connection history"
|
||||
icon={activeConnection?.id === latestConnectionId ? 'chevronDown' : 'pin'}
|
||||
icon={activeConnection?.id === latestConnectionId ? 'chevron_down' : 'pin'}
|
||||
className="ml-auto"
|
||||
size="sm"
|
||||
iconSize="md"
|
||||
|
||||
@@ -87,7 +87,7 @@ export const RecentResponsesDropdown = function ResponsePane({
|
||||
>
|
||||
<IconButton
|
||||
title="Show response history"
|
||||
icon={activeResponse?.id === latestResponseId ? 'chevronDown' : 'pin'}
|
||||
icon={activeResponse?.id === latestResponseId ? 'chevron_down' : 'pin'}
|
||||
className="m-0.5"
|
||||
size="sm"
|
||||
iconSize="md"
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
BODY_TYPE_NONE,
|
||||
BODY_TYPE_OTHER,
|
||||
BODY_TYPE_XML,
|
||||
} from '../lib/models';
|
||||
} from '../lib/model_util';
|
||||
import { BasicAuth } from './BasicAuth';
|
||||
import { BearerAuth } from './BearerAuth';
|
||||
import { BinaryFileEditor } from './BinaryFileEditor';
|
||||
|
||||
@@ -21,7 +21,7 @@ export function ResponseInfo({ response }: Props) {
|
||||
<IconButton
|
||||
iconSize="sm"
|
||||
className="inline-block w-auto ml-1 !h-auto opacity-50 hover:opacity-100"
|
||||
icon="externalLink"
|
||||
icon="external_link"
|
||||
onClick={() => open(response.url)}
|
||||
title="Open in browser"
|
||||
/>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createGlobalState } from 'react-use';
|
||||
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
||||
import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse';
|
||||
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
||||
import { isResponseLoading } from '../lib/models';
|
||||
import { isResponseLoading } from '../lib/model_util';
|
||||
import { Banner } from './core/Banner';
|
||||
import { CountBadge } from './core/CountBadge';
|
||||
import { DurationTag } from './core/DurationTag';
|
||||
|
||||
@@ -38,18 +38,18 @@ const icons: IconProps['icon'][] = [
|
||||
'info',
|
||||
'box',
|
||||
'update',
|
||||
'alert',
|
||||
'arrowBigRightDash',
|
||||
'alert_triangle',
|
||||
'arrow_big_right_dash',
|
||||
'download',
|
||||
'copy',
|
||||
'magicWand',
|
||||
'magic_wand',
|
||||
'settings',
|
||||
'trash',
|
||||
'sparkles',
|
||||
'pencil',
|
||||
'paste',
|
||||
'search',
|
||||
'sendHorizontal',
|
||||
'send_horizontal',
|
||||
];
|
||||
|
||||
export function SettingsAppearance() {
|
||||
|
||||
@@ -31,18 +31,18 @@ const icons: IconProps['icon'][] = [
|
||||
'info',
|
||||
'box',
|
||||
'update',
|
||||
'alert',
|
||||
'arrowBigRightDash',
|
||||
'alert_triangle',
|
||||
'arrow_big_right_dash',
|
||||
'download',
|
||||
'copy',
|
||||
'magicWand',
|
||||
'magic_wand',
|
||||
'settings',
|
||||
'trash',
|
||||
'sparkles',
|
||||
'pencil',
|
||||
'paste',
|
||||
'search',
|
||||
'sendHorizontal',
|
||||
'send_horizontal',
|
||||
];
|
||||
|
||||
export function SettingsDesign() {
|
||||
|
||||
@@ -64,13 +64,13 @@ export function SettingsDropdown() {
|
||||
{
|
||||
key: 'import-data',
|
||||
label: 'Import Data',
|
||||
leftSlot: <Icon icon="folderInput" />,
|
||||
leftSlot: <Icon icon="folder_input" />,
|
||||
onSelect: () => importData.mutate(),
|
||||
},
|
||||
{
|
||||
key: 'export-data',
|
||||
label: 'Export Data',
|
||||
leftSlot: <Icon icon="folderOutput" />,
|
||||
leftSlot: <Icon icon="folder_output" />,
|
||||
onSelect: () => exportData.mutate(),
|
||||
},
|
||||
{ type: 'separator', label: `Yaak v${appInfo.version}` },
|
||||
@@ -84,14 +84,14 @@ export function SettingsDropdown() {
|
||||
key: 'feedback',
|
||||
label: 'Feedback',
|
||||
leftSlot: <Icon icon="chat" />,
|
||||
rightSlot: <Icon icon="externalLink" />,
|
||||
rightSlot: <Icon icon="external_link" />,
|
||||
onSelect: () => open('https://yaak.app/roadmap'),
|
||||
},
|
||||
{
|
||||
key: 'changelog',
|
||||
label: 'Changelog',
|
||||
leftSlot: <Icon icon="cake" />,
|
||||
rightSlot: <Icon icon="externalLink" />,
|
||||
rightSlot: <Icon icon="external_link" />,
|
||||
onSelect: () => open(`https://yaak.app/changelog/${appInfo.version}`),
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -34,7 +34,7 @@ import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||
import { useWorkspaces } from '../hooks/useWorkspaces';
|
||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||
import { isResponseLoading } from '../lib/models';
|
||||
import { isResponseLoading } from '../lib/model_util';
|
||||
import { getHttpRequest } from '../lib/store';
|
||||
import type { DropdownItem } from './core/Dropdown';
|
||||
import { ContextMenu } from './core/Dropdown';
|
||||
@@ -433,32 +433,31 @@ export function Sidebar({ className }: Props) {
|
||||
onBlur={handleBlur}
|
||||
tabIndex={hidden ? -1 : 0}
|
||||
onContextMenu={handleMainContextMenu}
|
||||
className={classNames(
|
||||
className,
|
||||
'h-full pb-3 overflow-y-scroll overflow-x-visible hide-scrollbars pt-2',
|
||||
)}
|
||||
className={classNames(className, 'h-full grid grid-rows-[minmax(0,1fr)_auto]')}
|
||||
>
|
||||
<ContextMenu
|
||||
triggerPosition={showMainContextMenu}
|
||||
items={mainContextMenuItems}
|
||||
onClose={() => setShowMainContextMenu(null)}
|
||||
/>
|
||||
<SidebarItems
|
||||
treeParentMap={treeParentMap}
|
||||
activeId={activeRequest?.id ?? null}
|
||||
selectedId={selectedId}
|
||||
selectedTree={selectedTree}
|
||||
isCollapsed={isCollapsed}
|
||||
tree={tree}
|
||||
focused={hasFocus}
|
||||
draggingId={draggingId}
|
||||
onSelect={handleSelect}
|
||||
hoveredIndex={hoveredIndex}
|
||||
hoveredTree={hoveredTree}
|
||||
handleMove={handleMove}
|
||||
handleEnd={handleEnd}
|
||||
handleDragStart={handleDragStart}
|
||||
/>
|
||||
<div className="pb-3 overflow-x-visible overflow-y-scroll pt-2">
|
||||
<ContextMenu
|
||||
triggerPosition={showMainContextMenu}
|
||||
items={mainContextMenuItems}
|
||||
onClose={() => setShowMainContextMenu(null)}
|
||||
/>
|
||||
<SidebarItems
|
||||
treeParentMap={treeParentMap}
|
||||
activeId={activeRequest?.id ?? null}
|
||||
selectedId={selectedId}
|
||||
selectedTree={selectedTree}
|
||||
isCollapsed={isCollapsed}
|
||||
tree={tree}
|
||||
focused={hasFocus}
|
||||
draggingId={draggingId}
|
||||
onSelect={handleSelect}
|
||||
hoveredIndex={hoveredIndex}
|
||||
hoveredTree={hoveredTree}
|
||||
handleMove={handleMove}
|
||||
handleEnd={handleEnd}
|
||||
handleDragStart={handleDragStart}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -741,7 +740,7 @@ function SidebarItem({
|
||||
{
|
||||
key: 'sendAll',
|
||||
label: 'Send All',
|
||||
leftSlot: <Icon icon="sendHorizontal" />,
|
||||
leftSlot: <Icon icon="send_horizontal" />,
|
||||
onSelect: () => sendManyRequests.mutate(child.children.map((c) => c.item.id)),
|
||||
},
|
||||
{
|
||||
@@ -784,7 +783,7 @@ function SidebarItem({
|
||||
label: 'Send',
|
||||
hotKeyAction: 'http_request.send',
|
||||
hotKeyLabelOnly: true, // Already bound in URL bar
|
||||
leftSlot: <Icon icon="sendHorizontal" />,
|
||||
leftSlot: <Icon icon="send_horizontal" />,
|
||||
onSelect: () => sendRequest.mutate(itemId),
|
||||
},
|
||||
...httpRequestActions.map((a) => ({
|
||||
@@ -822,7 +821,7 @@ function SidebarItem({
|
||||
{
|
||||
key: 'moveWorkspace',
|
||||
label: 'Move',
|
||||
leftSlot: <Icon icon="arrowRightCircle" />,
|
||||
leftSlot: <Icon icon="arrow_right_circle" />,
|
||||
hidden: workspaces.length <= 1,
|
||||
onSelect: moveToWorkspace.mutate,
|
||||
},
|
||||
@@ -882,7 +881,7 @@ function SidebarItem({
|
||||
{itemModel === 'folder' && (
|
||||
<Icon
|
||||
size="sm"
|
||||
icon="chevronRight"
|
||||
icon="chevron_right"
|
||||
className={classNames(
|
||||
'text-text-subtlest',
|
||||
'transition-transform',
|
||||
|
||||
@@ -31,10 +31,10 @@ export function SidebarActions() {
|
||||
className="pointer-events-auto"
|
||||
size="sm"
|
||||
title="Show sidebar"
|
||||
icon={hidden ? 'leftPanelHidden' : 'leftPanelVisible'}
|
||||
icon={hidden ? 'left_panel_hidden' : 'left_panel_visible'}
|
||||
/>
|
||||
<CreateDropdown hotKeyAction="http_request.create">
|
||||
<IconButton size="sm" icon="plusCircle" title="Add Resource" />
|
||||
<IconButton size="sm" icon="plus_circle" title="Add Resource" />
|
||||
</CreateDropdown>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { createContext, useContext, useMemo, useRef, useState } from 'react';
|
||||
import type { ShowToastRequest } from '../../plugin-runtime-types/src';
|
||||
import type { ShowToastRequest } from '@yaakapp/api';
|
||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import type { ToastProps } from './core/Toast';
|
||||
import { Toast } from './core/Toast';
|
||||
import { generateId } from '../lib/generateId';
|
||||
import { Portal } from './Portal';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
|
||||
type ToastEntry = {
|
||||
id?: string;
|
||||
@@ -93,7 +93,7 @@ export const Toasts = () => {
|
||||
const { toasts } = useContext(ToastContext);
|
||||
return (
|
||||
<Portal name="toasts">
|
||||
<div className="absolute right-0 bottom-0 z-10">
|
||||
<div className="absolute right-0 bottom-0 z-20">
|
||||
<AnimatePresence>
|
||||
{toasts.map((props: PrivateToastEntry) => (
|
||||
<ToastInstance key={props.id} {...props} />
|
||||
|
||||
@@ -37,7 +37,7 @@ export const UrlBar = memo(function UrlBar({
|
||||
onCancel,
|
||||
onMethodChange,
|
||||
onPaste,
|
||||
submitIcon = 'sendHorizontal',
|
||||
submitIcon = 'send_horizontal',
|
||||
autocomplete,
|
||||
rightSlot,
|
||||
isLoading,
|
||||
|
||||
@@ -26,7 +26,7 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
|
||||
<CookieDropdown />
|
||||
<HStack>
|
||||
<WorkspaceActionsDropdown />
|
||||
<Icon icon="chevronRight" className="text-text-subtle" />
|
||||
<Icon icon="chevron_right" className="text-text-subtle" />
|
||||
<EnvironmentActionsDropdown className="w-auto pointer-events-auto" />
|
||||
</HStack>
|
||||
</HStack>
|
||||
|
||||
@@ -14,7 +14,7 @@ export function Banner({ children, className, color = 'secondary' }: Props) {
|
||||
className={classNames(
|
||||
className,
|
||||
`x-theme-banner--${color}`,
|
||||
'border border-dashed border-border-subtle bg-surface-highlight',
|
||||
'border border-dashed border-border-subtle bg-surface',
|
||||
'italic px-3 py-2 rounded select-auto cursor-text',
|
||||
'overflow-x-auto text-text',
|
||||
)}
|
||||
|
||||
@@ -108,6 +108,11 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
||||
className={classes}
|
||||
disabled={disabled || isLoading}
|
||||
onClick={onClick}
|
||||
onDoubleClick={(e) => {
|
||||
// Kind of a hack? This prevents double-clicks from going through buttons. For example, when
|
||||
// double-clicking the workspace header to toggle window maximization
|
||||
e.stopPropagation();
|
||||
}}
|
||||
title={fullTitle}
|
||||
{...props}
|
||||
>
|
||||
@@ -126,7 +131,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button
|
||||
{children}
|
||||
</div>
|
||||
{rightSlot && <div className="ml-1">{rightSlot}</div>}
|
||||
{forDropdown && <Icon icon="chevronDown" size={size} className="ml-1 -mr-1" />}
|
||||
{forDropdown && <Icon icon="chevron_down" size={size} className="ml-1 -mr-1" />}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -191,7 +191,7 @@ export const ContextMenu = forwardRef<DropdownRef, ContextMenuProps>(function Co
|
||||
|
||||
return (
|
||||
<Menu
|
||||
isOpen // Always open because we return null if not
|
||||
isOpen={true} // Always open because we return null if not
|
||||
className={className}
|
||||
ref={ref}
|
||||
items={items}
|
||||
@@ -361,29 +361,38 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
||||
}>(() => {
|
||||
if (triggerShape == null) return { containerStyles: {}, triangleStyles: null };
|
||||
|
||||
const menuMarginY = 5;
|
||||
const docRect = document.documentElement.getBoundingClientRect();
|
||||
const width = triggerShape.right - triggerShape.left;
|
||||
const heightAbove = triggerShape.top;
|
||||
const heightBelow = docRect.height - triggerShape.bottom;
|
||||
const hSpaceRemaining = docRect.width - triggerShape.left;
|
||||
const top = triggerShape.bottom + 5;
|
||||
const onRight = hSpaceRemaining < 200;
|
||||
const upsideDown = heightAbove > heightBelow && heightBelow < 200;
|
||||
const horizontalSpaceRemaining = docRect.width - triggerShape.left;
|
||||
const top = triggerShape.bottom;
|
||||
const onRight = horizontalSpaceRemaining < 200;
|
||||
const upsideDown = heightBelow < heightAbove && heightBelow < items.length * 25 + 20 + 200;
|
||||
const triggerWidth = triggerShape.right - triggerShape.left;
|
||||
const containerStyles = {
|
||||
top: !upsideDown ? top : undefined,
|
||||
bottom: upsideDown ? docRect.height - top : undefined,
|
||||
top: !upsideDown ? top + menuMarginY : undefined,
|
||||
bottom: upsideDown
|
||||
? docRect.height - top - (triggerShape.top - triggerShape.bottom) + menuMarginY
|
||||
: undefined,
|
||||
right: onRight ? docRect.width - triggerShape.right : undefined,
|
||||
left: !onRight ? triggerShape.left : undefined,
|
||||
minWidth: fullWidth ? triggerWidth : undefined,
|
||||
maxWidth: '40rem',
|
||||
};
|
||||
const size = { top: '-0.2rem', width: '0.4rem', height: '0.4rem' };
|
||||
const triangleStyles = onRight
|
||||
? { right: width / 2, marginRight: '-0.2rem', ...size }
|
||||
: { left: width / 2, marginLeft: '-0.2rem', ...size };
|
||||
const triangleStyles: CSSProperties = {
|
||||
width: '0.4rem',
|
||||
height: '0.4rem',
|
||||
...(onRight
|
||||
? { right: width / 2, marginRight: '-0.2rem' }
|
||||
: { left: width / 2, marginLeft: '-0.2rem' }),
|
||||
...(upsideDown
|
||||
? { bottom: '-0.2rem', rotate: '225deg' }
|
||||
: { top: '-0.2rem', rotate: '45deg' }),
|
||||
};
|
||||
return { containerStyles, triangleStyles };
|
||||
}, [fullWidth, triggerShape]);
|
||||
}, [fullWidth, items.length, triggerShape]);
|
||||
|
||||
const filteredItems = useMemo(
|
||||
() => items.filter((i) => getNodeText(i.label).toLowerCase().includes(filter.toLowerCase())),
|
||||
@@ -415,7 +424,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
||||
),
|
||||
)}
|
||||
{isOpen && (
|
||||
<Overlay open variant="transparent" portalName="dropdown" zIndex={50}>
|
||||
<Overlay open={true} variant="transparent" portalName="dropdown" zIndex={50}>
|
||||
<div className="x-theme-menu">
|
||||
<div tabIndex={-1} aria-hidden className="fixed inset-0 z-30" onClick={handleClose} />
|
||||
<motion.div
|
||||
@@ -433,7 +442,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
||||
<span
|
||||
aria-hidden
|
||||
style={triangleStyles}
|
||||
className="bg-surface absolute rotate-45 border-border-subtle border-t border-l"
|
||||
className="bg-surface absolute border-border-subtle border-t border-l"
|
||||
/>
|
||||
)}
|
||||
{containerStyles && (
|
||||
@@ -443,7 +452,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle'>, MenuPro
|
||||
className={classNames(
|
||||
className,
|
||||
'h-auto bg-surface rounded-md shadow-lg py-1.5 border',
|
||||
'border-border-subtle overflow-auto mb-1 mx-0.5',
|
||||
'border-border-subtle overflow-auto mx-0.5',
|
||||
)}
|
||||
>
|
||||
{filter && (
|
||||
|
||||
@@ -346,7 +346,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
key="format"
|
||||
size="sm"
|
||||
title="Reformat contents"
|
||||
icon="magicWand"
|
||||
icon="magic_wand"
|
||||
variant="border"
|
||||
className={classNames(actionClassName)}
|
||||
onClick={() => {
|
||||
|
||||
@@ -4,62 +4,69 @@ import type { HTMLAttributes } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
const icons = {
|
||||
alert: lucide.AlertTriangleIcon,
|
||||
alert_triangle: lucide.AlertTriangleIcon,
|
||||
archive: lucide.ArchiveIcon,
|
||||
arrowBigDownDash: lucide.ArrowBigDownDashIcon,
|
||||
arrowRightCircle: lucide.ArrowRightCircleIcon,
|
||||
arrowBigLeftDash: lucide.ArrowBigLeftDashIcon,
|
||||
arrowBigRight: lucide.ArrowBigRightIcon,
|
||||
arrowBigRightDash: lucide.ArrowBigRightDashIcon,
|
||||
arrowBigUpDash: lucide.ArrowBigUpDashIcon,
|
||||
arrowDown: lucide.ArrowDownIcon,
|
||||
arrowDownToDot: lucide.ArrowDownToDotIcon,
|
||||
arrowUp: lucide.ArrowUpIcon,
|
||||
arrowUpDown: lucide.ArrowUpDownIcon,
|
||||
arrowUpFromDot: lucide.ArrowUpFromDotIcon,
|
||||
arrow_big_down_dash: lucide.ArrowBigDownDashIcon,
|
||||
arrow_right_circle: lucide.ArrowRightCircleIcon,
|
||||
arrow_big_left_dash: lucide.ArrowBigLeftDashIcon,
|
||||
arrow_big_right: lucide.ArrowBigRightIcon,
|
||||
arrow_big_right_dash: lucide.ArrowBigRightDashIcon,
|
||||
arrow_big_up_dash: lucide.ArrowBigUpDashIcon,
|
||||
arrow_down: lucide.ArrowDownIcon,
|
||||
arrow_down_to_dot: lucide.ArrowDownToDotIcon,
|
||||
arrow_up: lucide.ArrowUpIcon,
|
||||
arrow_up_down: lucide.ArrowUpDownIcon,
|
||||
arrow_up_from_dot: lucide.ArrowUpFromDotIcon,
|
||||
badge_check: lucide.BadgeCheckIcon,
|
||||
box: lucide.BoxIcon,
|
||||
cake: lucide.CakeIcon,
|
||||
chat: lucide.MessageSquare,
|
||||
check: lucide.CheckIcon,
|
||||
checkCircle: lucide.CheckCircleIcon,
|
||||
chevronDown: lucide.ChevronDownIcon,
|
||||
chevronRight: lucide.ChevronRightIcon,
|
||||
check_circle: lucide.CheckCircleIcon,
|
||||
chevron_down: lucide.ChevronDownIcon,
|
||||
chevron_right: lucide.ChevronRightIcon,
|
||||
circle_alert: lucide.CircleAlertIcon,
|
||||
cloud: lucide.CloudIcon,
|
||||
code: lucide.CodeIcon,
|
||||
cookie: lucide.CookieIcon,
|
||||
copy: lucide.CopyIcon,
|
||||
copyCheck: lucide.CopyCheck,
|
||||
copy_check: lucide.CopyCheck,
|
||||
download: lucide.DownloadIcon,
|
||||
externalLink: lucide.ExternalLinkIcon,
|
||||
external_link: lucide.ExternalLinkIcon,
|
||||
eye: lucide.EyeIcon,
|
||||
eyeClosed: lucide.EyeOffIcon,
|
||||
fileCode: lucide.FileCodeIcon,
|
||||
eye_closed: lucide.EyeOffIcon,
|
||||
file_code: lucide.FileCodeIcon,
|
||||
filter: lucide.FilterIcon,
|
||||
flask: lucide.FlaskConicalIcon,
|
||||
folderInput: lucide.FolderInputIcon,
|
||||
folderOutput: lucide.FolderOutputIcon,
|
||||
gripVertical: lucide.GripVerticalIcon,
|
||||
folder_input: lucide.FolderInputIcon,
|
||||
folder_output: lucide.FolderOutputIcon,
|
||||
git_branch: lucide.GitBranchIcon,
|
||||
git_commit: lucide.GitCommitIcon,
|
||||
git_commit_vertical: lucide.GitCommitVerticalIcon,
|
||||
git_pull_request: lucide.GitPullRequestIcon,
|
||||
git_fork: lucide.GitForkIcon,
|
||||
grip_vertical: lucide.GripVerticalIcon,
|
||||
hand: lucide.HandIcon,
|
||||
help: lucide.CircleHelpIcon,
|
||||
house: lucide.HomeIcon,
|
||||
info: lucide.InfoIcon,
|
||||
keyboard: lucide.KeyboardIcon,
|
||||
leftPanelHidden: lucide.PanelLeftOpenIcon,
|
||||
leftPanelVisible: lucide.PanelLeftCloseIcon,
|
||||
magicWand: lucide.Wand2Icon,
|
||||
left_panel_hidden: lucide.PanelLeftOpenIcon,
|
||||
left_panel_visible: lucide.PanelLeftCloseIcon,
|
||||
magic_wand: lucide.Wand2Icon,
|
||||
minus: lucide.MinusIcon,
|
||||
moon: lucide.MoonIcon,
|
||||
moreVertical: lucide.MoreVerticalIcon,
|
||||
more_vertical: lucide.MoreVerticalIcon,
|
||||
paste: lucide.ClipboardPasteIcon,
|
||||
pencil: lucide.PencilIcon,
|
||||
pin: lucide.PinIcon,
|
||||
plug: lucide.Plug,
|
||||
plus: lucide.PlusIcon,
|
||||
plusCircle: lucide.PlusCircleIcon,
|
||||
plus_circle: lucide.PlusCircleIcon,
|
||||
refresh: lucide.RefreshCwIcon,
|
||||
save: lucide.SaveIcon,
|
||||
search: lucide.SearchIcon,
|
||||
sendHorizontal: lucide.SendHorizonalIcon,
|
||||
settings2: lucide.Settings2Icon,
|
||||
send_horizontal: lucide.SendHorizonalIcon,
|
||||
settings: lucide.SettingsIcon,
|
||||
sparkles: lucide.SparklesIcon,
|
||||
sun: lucide.SunIcon,
|
||||
|
||||
@@ -193,7 +193,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
||||
className="mr-0.5 group/obscure !h-auto my-0.5"
|
||||
iconClassName="text-text-subtle group-hover/obscure:text"
|
||||
iconSize="sm"
|
||||
icon={obscured ? 'eye' : 'eyeClosed'}
|
||||
icon={obscured ? 'eye' : 'eye_closed'}
|
||||
onClick={() => setObscured((o) => !o)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -101,7 +101,7 @@ export const JsonAttributeTree = ({
|
||||
<button className="group relative flex items-center pl-4 w-full" onClick={toggleExpanded}>
|
||||
<Icon
|
||||
size="xs"
|
||||
icon="chevronRight"
|
||||
icon="chevron_right"
|
||||
className={classNames(
|
||||
'left-0 absolute transition-transform flex items-center',
|
||||
'text-text-subtlest group-hover:text-text-subtle',
|
||||
|
||||
@@ -22,7 +22,7 @@ export function Link({ href, children, className, ...other }: Props) {
|
||||
{...other}
|
||||
>
|
||||
<span className="underline">{children}</span>
|
||||
<Icon className="inline absolute right-0.5 top-0.5" size="xs" icon="externalLink" />
|
||||
<Icon className="inline absolute right-0.5 top-0.5" size="xs" icon="external_link" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ function PairEditorRow({
|
||||
'justify-center opacity-0 group-hover:opacity-70',
|
||||
)}
|
||||
>
|
||||
<Icon size="sm" icon="gripVertical" className="pointer-events-none" />
|
||||
<Icon size="sm" icon="grip_vertical" className="pointer-events-none" />
|
||||
</div>
|
||||
) : (
|
||||
<span className="w-3" />
|
||||
@@ -545,7 +545,7 @@ function PairEditorRow({
|
||||
<IconButton
|
||||
iconSize="sm"
|
||||
size="xs"
|
||||
icon={isLast ? 'empty' : 'chevronDown'}
|
||||
icon={isLast ? 'empty' : 'chevron_down'}
|
||||
title="Select form data type"
|
||||
/>
|
||||
</RadioDropdown>
|
||||
@@ -563,7 +563,7 @@ function PairEditorRow({
|
||||
<IconButton
|
||||
iconSize="sm"
|
||||
size="xs"
|
||||
icon={isLast ? 'empty' : 'chevronDown'}
|
||||
icon={isLast ? 'empty' : 'chevron_down'}
|
||||
title="Select form data type"
|
||||
/>
|
||||
</Dropdown>
|
||||
|
||||
@@ -33,7 +33,7 @@ export const PairOrBulkEditor = forwardRef<PairEditorRef, Props>(function PairOr
|
||||
'bg-surface text-text-subtle hover:text group-hover/wrapper:opacity-100',
|
||||
)}
|
||||
onClick={() => setUseBulk((b) => !b)}
|
||||
icon={useBulk ? 'table' : 'fileCode'}
|
||||
icon={useBulk ? 'table' : 'file_code'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -143,7 +143,7 @@ export const PlainInput = forwardRef<HTMLInputElement, PlainInputProps>(function
|
||||
className="mr-0.5 group/obscure !h-auto my-0.5"
|
||||
iconClassName="text-text-subtle group-hover/obscure:text"
|
||||
iconSize="sm"
|
||||
icon={obscured ? 'eye' : 'eyeClosed'}
|
||||
icon={obscured ? 'eye' : 'eye_closed'}
|
||||
onClick={() => setObscured((o) => !o)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -100,10 +100,10 @@ export function Tabs({
|
||||
>
|
||||
{option && 'shortLabel' in option
|
||||
? option.shortLabel
|
||||
: option?.label ?? 'Unknown'}
|
||||
: (option?.label ?? 'Unknown')}
|
||||
<Icon
|
||||
size="sm"
|
||||
icon="chevronDown"
|
||||
icon="chevron_down"
|
||||
className={classNames('ml-1', isActive ? 'text-text-subtle' : 'opacity-50')}
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ShowToastRequest } from '@yaakapp/api';
|
||||
import classNames from 'classnames';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -15,27 +16,23 @@ export interface ToastProps {
|
||||
className?: string;
|
||||
timeout: number | null;
|
||||
action?: ReactNode;
|
||||
variant?: 'custom' | 'copied' | 'success' | 'info' | 'warning' | 'error';
|
||||
icon?: ShowToastRequest['icon'];
|
||||
color?: ShowToastRequest['color'];
|
||||
}
|
||||
|
||||
const ICONS: Record<NonNullable<ToastProps['variant']>, IconProps['icon'] | null> = {
|
||||
const ICONS: Record<NonNullable<ToastProps['color']>, IconProps['icon'] | null> = {
|
||||
custom: null,
|
||||
copied: 'copyCheck',
|
||||
warning: 'alert',
|
||||
error: 'alert',
|
||||
default: 'info',
|
||||
danger: 'alert_triangle',
|
||||
info: 'info',
|
||||
success: 'checkCircle',
|
||||
notice: 'alert_triangle',
|
||||
primary: 'info',
|
||||
secondary: 'info',
|
||||
success: 'check_circle',
|
||||
warning: 'alert_triangle',
|
||||
};
|
||||
|
||||
export function Toast({
|
||||
children,
|
||||
className,
|
||||
open,
|
||||
onClose,
|
||||
timeout,
|
||||
action,
|
||||
variant = 'info',
|
||||
}: ToastProps) {
|
||||
export function Toast({ children, open, onClose, timeout, action, icon, color }: ToastProps) {
|
||||
useKey(
|
||||
'Escape',
|
||||
() => {
|
||||
@@ -45,8 +42,9 @@ export function Toast({
|
||||
{},
|
||||
[open],
|
||||
);
|
||||
color = color ?? 'default';
|
||||
|
||||
const icon = variant in ICONS && ICONS[variant];
|
||||
const toastIcon = icon ?? (color in ICONS && ICONS[color]);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
@@ -54,55 +52,46 @@ export function Toast({
|
||||
animate={{ opacity: 100, right: 0 }}
|
||||
exit={{ opacity: 0, right: '-100%' }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className={classNames(
|
||||
className,
|
||||
'x-theme-toast',
|
||||
'pointer-events-auto',
|
||||
'relative bg-surface pointer-events-auto',
|
||||
'rounded-lg',
|
||||
'border border-border-subtle shadow-lg',
|
||||
'max-w-[calc(100vw-5rem)] max-h-[calc(100vh-6rem)]',
|
||||
'w-[22rem] max-h-[80vh]',
|
||||
'm-2 grid grid-cols-[1fr_auto]',
|
||||
'text',
|
||||
)}
|
||||
className={classNames('bg-surface m-2 rounded-lg')}
|
||||
>
|
||||
<div className="px-3 py-3 flex items-center gap-2">
|
||||
{icon && (
|
||||
<Icon
|
||||
icon={icon}
|
||||
className={classNames(
|
||||
variant === 'success' && 'text-success',
|
||||
variant === 'warning' && 'text-warning',
|
||||
variant === 'error' && 'text-danger',
|
||||
variant === 'copied' && 'text-primary',
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={classNames(
|
||||
`x-theme-toast x-theme-toast--${color}`,
|
||||
'pointer-events-auto overflow-hidden',
|
||||
'relative pointer-events-auto bg-surface text-text rounded-lg',
|
||||
'border border-border shadow-lg',
|
||||
'grid grid-cols-[1fr_auto]',
|
||||
'text',
|
||||
)}
|
||||
<VStack space={2}>
|
||||
<div>{children}</div>
|
||||
{action}
|
||||
</VStack>
|
||||
</div>
|
||||
|
||||
<IconButton
|
||||
color="custom"
|
||||
className="opacity-60"
|
||||
title="Dismiss"
|
||||
icon="x"
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{timeout != null && (
|
||||
<div className="w-full absolute bottom-0 left-0 right-0">
|
||||
<motion.div
|
||||
className="bg-surface-highlight h-0.5"
|
||||
initial={{ width: '100%' }}
|
||||
animate={{ width: '0%', opacity: 0.2 }}
|
||||
transition={{ duration: timeout / 1000, ease: 'linear' }}
|
||||
/>
|
||||
>
|
||||
<div className="px-3 py-3 flex items-center gap-2">
|
||||
{toastIcon && <Icon icon={toastIcon} className="text-text-subtle" />}
|
||||
<VStack space={2}>
|
||||
<div>{children}</div>
|
||||
{action}
|
||||
</VStack>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<IconButton
|
||||
color={color}
|
||||
variant="border"
|
||||
className="opacity-60 border-0"
|
||||
title="Dismiss"
|
||||
icon="x"
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{timeout != null && (
|
||||
<div className="w-full absolute bottom-0 left-0 right-0">
|
||||
<motion.div
|
||||
className="bg-surface-highlight h-[3px]"
|
||||
initial={{ width: '100%' }}
|
||||
animate={{ width: '0%', opacity: 0.2 }}
|
||||
transition={{ duration: timeout / 1000, ease: 'linear' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useSaveResponse } from '../../hooks/useSaveResponse';
|
||||
import type { HttpResponse } from '@yaakapp/api';
|
||||
import { getContentTypeHeader } from '../../lib/models';
|
||||
import { getContentTypeHeader } from '../../lib/model_util';
|
||||
import { Banner } from '../core/Banner';
|
||||
import { Button } from '../core/Button';
|
||||
import { InlineCode } from '../core/InlineCode';
|
||||
|
||||
@@ -15,7 +15,8 @@ export function useCopy({ disableToast }: { disableToast?: boolean } = {}) {
|
||||
if (text != '' && !disableToast) {
|
||||
toast.show({
|
||||
id: 'copied',
|
||||
variant: 'copied',
|
||||
color: 'secondary',
|
||||
icon: 'copy',
|
||||
message: 'Copied to clipboard',
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { DropdownItem } from '../components/core/Dropdown';
|
||||
import { Icon } from '../components/core/Icon';
|
||||
import { BODY_TYPE_GRAPHQL } from '../lib/models';
|
||||
import { BODY_TYPE_GRAPHQL } from '../lib/model_util';
|
||||
import { useCreateFolder } from './useCreateFolder';
|
||||
import { useCreateGrpcRequest } from './useCreateGrpcRequest';
|
||||
import { useCreateHttpRequest } from './useCreateHttpRequest';
|
||||
|
||||
@@ -33,7 +33,7 @@ export function useExportData() {
|
||||
activeWorkspace={activeWorkspace}
|
||||
onSuccess={() => {
|
||||
toast.show({
|
||||
variant: 'success',
|
||||
color: 'success',
|
||||
message: 'Data export successful',
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -39,7 +39,7 @@ export function useImportCurl() {
|
||||
}
|
||||
|
||||
toast.show({
|
||||
variant: 'success',
|
||||
color: 'success',
|
||||
message: `${verb} request from Curl`,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -51,7 +51,7 @@ export function useImportQuerystring(requestId: string) {
|
||||
if (additionalUrlParameters.length > 0) {
|
||||
toast.show({
|
||||
id: 'querystring-imported',
|
||||
variant: 'info',
|
||||
color: 'info',
|
||||
message: `Imported ${additionalUrlParameters.length} ${pluralize('param', additionalUrlParameters.length)} from URL`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isResponseLoading } from '../lib/models';
|
||||
import { isResponseLoading } from '../lib/model_util';
|
||||
import { useLatestHttpResponse } from './useLatestHttpResponse';
|
||||
|
||||
export function useIsResponseLoading(requestId: string | null): boolean {
|
||||
|
||||
@@ -27,7 +27,7 @@ export function useNotificationToast() {
|
||||
id: payload.id,
|
||||
timeout: null,
|
||||
message: payload.message,
|
||||
variant: 'custom',
|
||||
color: 'custom',
|
||||
onClose: () => markRead(payload.id),
|
||||
action:
|
||||
actionLabel && actionUrl ? (
|
||||
|
||||
@@ -5,7 +5,7 @@ import slugify from 'slugify';
|
||||
import { InlineCode } from '../components/core/InlineCode';
|
||||
import { useToast } from '../components/ToastContext';
|
||||
import type { HttpResponse } from '@yaakapp/api';
|
||||
import { getContentTypeHeader } from '../lib/models';
|
||||
import { getContentTypeHeader } from '../lib/model_util';
|
||||
import { getHttpRequest } from '../lib/store';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { readFile } from '@tauri-apps/plugin-fs';
|
||||
import type { HttpResponse } from '@yaakapp/api';
|
||||
import { getCharsetFromContentType } from './models';
|
||||
import { getCharsetFromContentType } from './model_util';
|
||||
|
||||
export async function getResponseBodyText(response: HttpResponse): Promise<string | null> {
|
||||
if (response.bodyPath) {
|
||||
|
||||
@@ -98,14 +98,23 @@ function templateTagColorVariables(color: YaakColor): Partial<CSSVariables> {
|
||||
};
|
||||
}
|
||||
|
||||
function toastColorVariables(color: YaakColor): Partial<CSSVariables> {
|
||||
return {
|
||||
text: color.lift(0.8),
|
||||
textSubtle: color,
|
||||
surface: color.translucify(0.9),
|
||||
surfaceHighlight: color.translucify(0.8),
|
||||
border: color.lift(0.3).translucify(0.6),
|
||||
};
|
||||
}
|
||||
|
||||
function bannerColorVariables(color: YaakColor): Partial<CSSVariables> {
|
||||
return {
|
||||
text: color.lift(0.8),
|
||||
textSubtle: color.translucify(0.3),
|
||||
textSubtlest: color,
|
||||
surface: color,
|
||||
surface: color.translucify(0.9),
|
||||
border: color.lift(0.3).translucify(0.4),
|
||||
surfaceHighlight: color.translucify(0.9),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -201,6 +210,15 @@ function bannerCSS(color: YaakColorKey, colors?: Partial<YaakColors>): string |
|
||||
);
|
||||
}
|
||||
|
||||
function toastCSS(color: YaakColorKey, colors?: Partial<YaakColors>): string | null {
|
||||
const yaakColor = colors?.[color];
|
||||
if (yaakColor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [variablesToCSS(`.x-theme-toast--${color}`, toastColorVariables(yaakColor))].join('\n\n');
|
||||
}
|
||||
|
||||
function templateTagCSS(color: YaakColorKey, colors?: Partial<YaakColors>): string | null {
|
||||
const yaakColor = colors?.[color];
|
||||
if (yaakColor == null) {
|
||||
@@ -253,6 +271,9 @@ export function getThemeCSS(theme: YaakTheme): string {
|
||||
...Object.keys(colors ?? {}).map((key) =>
|
||||
bannerCSS(key as YaakColorKey, theme.components?.banner ?? colors),
|
||||
),
|
||||
...Object.keys(colors ?? {}).map((key) =>
|
||||
toastCSS(key as YaakColorKey, theme.components?.banner ?? colors),
|
||||
),
|
||||
...Object.keys(colors ?? {}).map((key) =>
|
||||
templateTagCSS(key as YaakColorKey, theme.components?.templateTag ?? colors),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user