Move request-related settings to workspace

This commit is contained in:
Gregory Schier
2024-01-15 11:52:36 -08:00
parent 3539642491
commit 77bf5a58d8
23 changed files with 207 additions and 148 deletions

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "\n UPDATE settings SET (\n follow_redirects,\n validate_certificates,\n theme,\n appearance\n ) = (?, ?, ?, ?) WHERE id = 'default';\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "09a8074f7ef8d734607f95391819e38f822488933905dcc7a7788bd184bc7796"
}

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "\n SELECT\n id,\n model,\n created_at,\n updated_at,\n follow_redirects,\n validate_certificates,\n request_timeout,\n theme,\n appearance\n FROM settings\n WHERE id = 'default'\n ", "query": "\n SELECT\n id,\n model,\n created_at,\n updated_at,\n name,\n description,\n setting_request_timeout,\n setting_follow_redirects,\n setting_validate_certificates,\n variables AS \"variables!: sqlx::types::Json<Vec<EnvironmentVariable>>\"\n FROM workspaces\n ",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -24,28 +24,33 @@
"type_info": "Datetime" "type_info": "Datetime"
}, },
{ {
"name": "follow_redirects", "name": "name",
"ordinal": 4, "ordinal": 4,
"type_info": "Bool" "type_info": "Text"
}, },
{ {
"name": "validate_certificates", "name": "description",
"ordinal": 5, "ordinal": 5,
"type_info": "Bool" "type_info": "Text"
}, },
{ {
"name": "request_timeout", "name": "setting_request_timeout",
"ordinal": 6, "ordinal": 6,
"type_info": "Int64" "type_info": "Int64"
}, },
{ {
"name": "theme", "name": "setting_follow_redirects",
"ordinal": 7, "ordinal": 7,
"type_info": "Text" "type_info": "Bool"
}, },
{ {
"name": "appearance", "name": "setting_validate_certificates",
"ordinal": 8, "ordinal": 8,
"type_info": "Bool"
},
{
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>",
"ordinal": 9,
"type_info": "Text" "type_info": "Text"
} }
], ],
@@ -61,8 +66,9 @@
false, false,
false, false,
false, false,
false,
false false
] ]
}, },
"hash": "f27d45f7ea2b04fc203e46a85be96a591a6495794dc042e1e2f3460c9ed65a5c" "hash": "20cf0f8b71e600bc40ee204b60a12b2f3728178f3b8788d2e5cc92821b74d470"
} }

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n UPDATE settings SET (\n theme,\n appearance\n ) = (?, ?) WHERE id = 'default';\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "21cf14623d646b0d96ba92392edc4d48dff601d04acee62d2a0c12b8e655787b"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO workspaces (id, name, description, variables)\n VALUES (?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n description = excluded.description,\n variables = excluded.variables\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "610223ad10b6e25926d486ba775a74b55625fcc4e6637d8a805d44ec3f3b9532"
}

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "\n SELECT id, model, created_at, updated_at, name, description,\n variables AS \"variables!: sqlx::types::Json<Vec<EnvironmentVariable>>\"\n FROM workspaces\n ", "query": "\n SELECT\n id,\n model,\n created_at,\n updated_at,\n theme,\n appearance\n FROM settings\n WHERE id = 'default'\n ",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -24,19 +24,14 @@
"type_info": "Datetime" "type_info": "Datetime"
}, },
{ {
"name": "name", "name": "theme",
"ordinal": 4, "ordinal": 4,
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "description", "name": "appearance",
"ordinal": 5, "ordinal": 5,
"type_info": "Text" "type_info": "Text"
},
{
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>",
"ordinal": 6,
"type_info": "Text"
} }
], ],
"parameters": { "parameters": {
@@ -48,9 +43,8 @@
false, false,
false, false,
false, false,
false,
false false
] ]
}, },
"hash": "5588db23df7f30dc75857e05395ebbcf2384e2ac0d7cb87f76d74c6d50781d7b" "hash": "6864f2a9032a630cd7c8310be5cb0517ec13d12489540a70b15f23b1e8de7b91"
} }

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "\n INSERT INTO workspaces (\n id,\n name,\n description,\n variables,\n setting_request_timeout,\n setting_follow_redirects,\n setting_validate_certificates\n )\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT (id) DO UPDATE SET\n updated_at = CURRENT_TIMESTAMP,\n name = excluded.name,\n description = excluded.description,\n variables = excluded.variables,\n setting_request_timeout = excluded.setting_request_timeout,\n setting_follow_redirects = excluded.setting_follow_redirects,\n setting_validate_certificates = excluded.setting_validate_certificates\n ",
"describe": {
"columns": [],
"parameters": {
"Right": 7
},
"nullable": []
},
"hash": "cae4e905515ddea1ec2cd685020241f06b49719085a695b897ef8ad409d2cef2"
}

View File

@@ -1,6 +1,6 @@
{ {
"db_name": "SQLite", "db_name": "SQLite",
"query": "\n SELECT id, model, created_at, updated_at, name, description,\n variables AS \"variables!: sqlx::types::Json<Vec<EnvironmentVariable>>\"\n FROM workspaces WHERE id = ?\n ", "query": "\n SELECT\n id,\n model,\n created_at,\n updated_at,\n name,\n description,\n setting_request_timeout,\n setting_follow_redirects,\n setting_validate_certificates,\n variables AS \"variables!: sqlx::types::Json<Vec<EnvironmentVariable>>\"\n FROM workspaces WHERE id = ?\n ",
"describe": { "describe": {
"columns": [ "columns": [
{ {
@@ -34,8 +34,23 @@
"type_info": "Text" "type_info": "Text"
}, },
{ {
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>", "name": "setting_request_timeout",
"ordinal": 6, "ordinal": 6,
"type_info": "Int64"
},
{
"name": "setting_follow_redirects",
"ordinal": 7,
"type_info": "Bool"
},
{
"name": "setting_validate_certificates",
"ordinal": 8,
"type_info": "Bool"
},
{
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>",
"ordinal": 9,
"type_info": "Null" "type_info": "Null"
} }
], ],
@@ -49,8 +64,11 @@
false, false,
false, false,
false, false,
false,
false,
false,
false false
] ]
}, },
"hash": "dbe457087a7bccbca4c1d673aa8e547df04530a7f860a6ccd4e20126a7cdfa4f" "hash": "e08fa4f9b2929f20a01d1dc43d6847a309d3e8c5b324df2d039d1c6e07e6eb2f"
} }

View File

@@ -0,0 +1,9 @@
-- Add existing request-related settings to workspace
ALTER TABLE workspaces ADD COLUMN setting_request_timeout INTEGER DEFAULT '0' NOT NULL;
ALTER TABLE workspaces ADD COLUMN setting_validate_certificates BOOLEAN DEFAULT TRUE NOT NULL;
ALTER TABLE workspaces ADD COLUMN setting_follow_redirects BOOLEAN DEFAULT TRUE NOT NULL;
-- Remove old settings that used to be global
ALTER TABLE settings DROP COLUMN request_timeout;
ALTER TABLE settings DROP COLUMN follow_redirects;
ALTER TABLE settings DROP COLUMN validate_certificates;

View File

@@ -15,7 +15,7 @@ use tauri::{AppHandle, Wry};
use crate::{emit_side_effect, models, render, response_err}; use crate::{emit_side_effect, models, render, response_err};
pub async fn actually_send_request( pub async fn send_http_request(
request: models::HttpRequest, request: models::HttpRequest,
response: &models::HttpResponse, response: &models::HttpResponse,
environment_id: &str, environment_id: &str,
@@ -35,12 +35,8 @@ pub async fn actually_send_request(
url_string = format!("http://{}", url_string); url_string = format!("http://{}", url_string);
} }
let settings = models::get_or_create_settings(pool)
.await
.expect("Failed to get settings");
let mut client_builder = reqwest::Client::builder() let mut client_builder = reqwest::Client::builder()
.redirect(match settings.follow_redirects { .redirect(match workspace.setting_follow_redirects {
true => Policy::limited(10), // TODO: Handle redirects natively true => Policy::limited(10), // TODO: Handle redirects natively
false => Policy::none(), false => Policy::none(),
}) })
@@ -48,13 +44,13 @@ pub async fn actually_send_request(
.brotli(true) .brotli(true)
.deflate(true) .deflate(true)
.referer(false) .referer(false)
.danger_accept_invalid_certs(!settings.validate_certificates) .danger_accept_invalid_certs(!workspace.setting_validate_certificates)
.connection_verbose(true) // TODO: Capture this log somehow .connection_verbose(true) // TODO: Capture this log somehow
.tls_info(true); .tls_info(true);
if settings.request_timeout > 0 { if workspace.setting_request_timeout > 0 {
client_builder = client_builder.timeout(Duration::from_millis( client_builder = client_builder.timeout(Duration::from_millis(
settings.request_timeout.unsigned_abs(), workspace.setting_request_timeout.unsigned_abs(),
)); ));
} }

View File

@@ -33,14 +33,14 @@ use window_ext::TrafficLightWindowExt;
use crate::analytics::{AnalyticsAction, AnalyticsResource}; use crate::analytics::{AnalyticsAction, AnalyticsResource};
use crate::plugin::{ImportResources, ImportResult}; use crate::plugin::{ImportResources, ImportResult};
use crate::send::actually_send_request; use crate::http::send_http_request;
use crate::updates::{update_mode_from_str, UpdateMode, YaakUpdater}; use crate::updates::{update_mode_from_str, UpdateMode, YaakUpdater};
mod analytics; mod analytics;
mod models; mod models;
mod plugin; mod plugin;
mod render; mod render;
mod send; mod http;
mod updates; mod updates;
mod window_ext; mod window_ext;
mod window_menu; mod window_menu;
@@ -84,7 +84,7 @@ async fn send_ephemeral_request(
let response = models::HttpResponse::new(); let response = models::HttpResponse::new();
let environment_id2 = environment_id.unwrap_or("n/a").to_string(); let environment_id2 = environment_id.unwrap_or("n/a").to_string();
request.id = "".to_string(); request.id = "".to_string();
actually_send_request(request, &response, &environment_id2, &app_handle, pool).await send_http_request(request, &response, &environment_id2, &app_handle, pool).await
} }
#[tauri::command] #[tauri::command]
@@ -199,7 +199,7 @@ async fn send_request(
tokio::spawn(async move { tokio::spawn(async move {
if let Err(e) = if let Err(e) =
actually_send_request(req, &response2, &environment_id2, &app_handle2, &pool2).await send_http_request(req, &response2, &environment_id2, &app_handle2, &pool2).await
{ {
response_err(&response2, e, &app_handle2, &pool2) response_err(&response2, e, &app_handle2, &pool2)
.await .await

View File

@@ -15,13 +15,8 @@ pub struct Settings {
pub model: String, pub model: String,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
// Settings
pub validate_certificates: bool,
pub follow_redirects: bool,
pub theme: String, pub theme: String,
pub appearance: String, pub appearance: String,
pub request_timeout: i64,
} }
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] #[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)]
@@ -34,6 +29,11 @@ pub struct Workspace {
pub name: String, pub name: String,
pub description: String, pub description: String,
pub variables: Json<Vec<EnvironmentVariable>>, pub variables: Json<Vec<EnvironmentVariable>>,
// Settings
pub setting_validate_certificates: bool,
pub setting_follow_redirects: bool,
pub setting_request_timeout: i64,
} }
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)] #[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Default)]
@@ -228,7 +228,16 @@ pub async fn find_workspaces(pool: &Pool<Sqlite>) -> Result<Vec<Workspace>, sqlx
sqlx::query_as!( sqlx::query_as!(
Workspace, Workspace,
r#" r#"
SELECT id, model, created_at, updated_at, name, description, SELECT
id,
model,
created_at,
updated_at,
name,
description,
setting_request_timeout,
setting_follow_redirects,
setting_validate_certificates,
variables AS "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>" variables AS "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>"
FROM workspaces FROM workspaces
"#, "#,
@@ -241,7 +250,16 @@ pub async fn get_workspace(id: &str, pool: &Pool<Sqlite>) -> Result<Workspace, s
sqlx::query_as!( sqlx::query_as!(
Workspace, Workspace,
r#" r#"
SELECT id, model, created_at, updated_at, name, description, SELECT
id,
model,
created_at,
updated_at,
name,
description,
setting_request_timeout,
setting_follow_redirects,
setting_validate_certificates,
variables AS "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>" variables AS "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>"
FROM workspaces WHERE id = ? FROM workspaces WHERE id = ?
"#, "#,
@@ -312,9 +330,6 @@ async fn get_settings(pool: &Pool<Sqlite>) -> Result<Settings, sqlx::Error> {
model, model,
created_at, created_at,
updated_at, updated_at,
follow_redirects,
validate_certificates,
request_timeout,
theme, theme,
appearance appearance
FROM settings FROM settings
@@ -349,14 +364,10 @@ pub async fn update_settings(
sqlx::query!( sqlx::query!(
r#" r#"
UPDATE settings SET ( UPDATE settings SET (
follow_redirects,
validate_certificates,
theme, theme,
appearance appearance
) = (?, ?, ?, ?) WHERE id = 'default'; ) = (?, ?) WHERE id = 'default';
"#, "#,
settings.follow_redirects,
settings.validate_certificates,
settings.theme, settings.theme,
settings.appearance, settings.appearance,
) )
@@ -750,18 +761,32 @@ pub async fn upsert_workspace(
let trimmed_name = workspace.name.trim(); let trimmed_name = workspace.name.trim();
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO workspaces (id, name, description, variables) INSERT INTO workspaces (
VALUES (?, ?, ?, ?) id,
name,
description,
variables,
setting_request_timeout,
setting_follow_redirects,
setting_validate_certificates
)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (id) DO UPDATE SET ON CONFLICT (id) DO UPDATE SET
updated_at = CURRENT_TIMESTAMP, updated_at = CURRENT_TIMESTAMP,
name = excluded.name, name = excluded.name,
description = excluded.description, description = excluded.description,
variables = excluded.variables variables = excluded.variables,
setting_request_timeout = excluded.setting_request_timeout,
setting_follow_redirects = excluded.setting_follow_redirects,
setting_validate_certificates = excluded.setting_validate_certificates
"#, "#,
id, id,
trimmed_name, trimmed_name,
workspace.description, workspace.description,
workspace.variables, workspace.variables,
workspace.setting_request_timeout,
workspace.setting_follow_redirects,
workspace.setting_validate_certificates,
) )
.execute(pool) .execute(pool)
.await?; .await?;

View File

@@ -12,6 +12,7 @@ import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
import { responsesQueryKey } from '../hooks/useResponses'; import { responsesQueryKey } from '../hooks/useResponses';
import { settingsQueryKey } from '../hooks/useSettings'; import { settingsQueryKey } from '../hooks/useSettings';
import { useSyncWindowTitle } from '../hooks/useSyncWindowTitle'; import { useSyncWindowTitle } from '../hooks/useSyncWindowTitle';
import { useSyncAppearance } from '../hooks/useSyncAppearance';
import { workspacesQueryKey } from '../hooks/useWorkspaces'; import { workspacesQueryKey } from '../hooks/useWorkspaces';
import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore'; import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore';
import type { HttpRequest, HttpResponse, Model, Workspace } from '../lib/models'; import type { HttpRequest, HttpResponse, Model, Workspace } from '../lib/models';
@@ -27,6 +28,8 @@ export function GlobalHooks() {
useRecentEnvironments(); useRecentEnvironments();
useRecentRequests(); useRecentRequests();
useSyncAppearance();
useSyncWindowTitle(); useSyncWindowTitle();
const queryClient = useQueryClient(); const queryClient = useQueryClient();

View File

@@ -4,7 +4,9 @@ import { memo, useCallback, useMemo, useState } from 'react';
import { createGlobalState } from 'react-use'; import { createGlobalState } from 'react-use';
import { useActiveRequest } from '../hooks/useActiveRequest'; import { useActiveRequest } from '../hooks/useActiveRequest';
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
import { useSettings } from '../hooks/useSettings';
import { useUpdateRequest } from '../hooks/useUpdateRequest'; import { useUpdateRequest } from '../hooks/useUpdateRequest';
import { useUpdateSettings } from '../hooks/useUpdateSettings';
import { tryFormatJson } from '../lib/formatters'; import { tryFormatJson } from '../lib/formatters';
import type { HttpHeader, HttpRequest, HttpUrlParameter } from '../lib/models'; import type { HttpHeader, HttpRequest, HttpUrlParameter } from '../lib/models';
import { import {
@@ -20,10 +22,13 @@ import {
} from '../lib/models'; } from '../lib/models';
import { BasicAuth } from './BasicAuth'; import { BasicAuth } from './BasicAuth';
import { BearerAuth } from './BearerAuth'; import { BearerAuth } from './BearerAuth';
import { Checkbox } from './core/Checkbox';
import { CountBadge } from './core/CountBadge'; import { CountBadge } from './core/CountBadge';
import { Editor } from './core/Editor'; import { Editor } from './core/Editor';
import type { TabItem } from './core/Tabs/Tabs'; import { Input } from './core/Input';
import { VStack } from './core/Stacks';
import { TabContent, Tabs } from './core/Tabs/Tabs'; import { TabContent, Tabs } from './core/Tabs/Tabs';
import type { TabItem } 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';

View File

@@ -7,7 +7,7 @@ import { VStack } from './core/Stacks';
export default function RouteError() { export default function RouteError() {
const error = useRouteError(); const error = useRouteError();
console.log("Error", error); console.log('Error', error);
const stringified = JSON.stringify(error); const stringified = JSON.stringify(error);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const message = (error as any).message ?? stringified; const message = (error as any).message ?? stringified;

View File

@@ -1,75 +1,73 @@
import classNames from 'classnames'; import classNames from 'classnames';
import { useActiveWorkspace } from '../hooks/useActiveWorkspace';
import { useSettings } from '../hooks/useSettings'; import { useSettings } from '../hooks/useSettings';
import { useTheme } from '../hooks/useTheme';
import { useUpdateSettings } from '../hooks/useUpdateSettings'; import { useUpdateSettings } from '../hooks/useUpdateSettings';
import type { Appearance } from '../lib/theme/window'; import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace';
import { Checkbox } from './core/Checkbox'; import { Checkbox } from './core/Checkbox';
import { Input } from './core/Input'; import { Input } from './core/Input';
import { VStack } from './core/Stacks'; import { HStack, VStack } from './core/Stacks';
export const SettingsDialog = () => { export const SettingsDialog = () => {
const { appearance, setAppearance } = useTheme(); const workspace = useActiveWorkspace();
const updateWorkspace = useUpdateWorkspace(workspace?.id ?? null);
const settings = useSettings(); const settings = useSettings();
const updateSettings = useUpdateSettings(); const updateSettings = useUpdateSettings();
if (settings == null) { if (settings == null || workspace == null) {
return null; return null;
} }
console.log('SETTINGS', settings);
return ( return (
<VStack space={2}> <VStack space={2}>
<div className="w-full gap-2 grid grid-cols-[auto_1fr] gap-x-6 auto-rows-[2rem] items-center"> <VStack className="w-full" space={3}>
<Checkbox <Checkbox
className="col-span-full" checked={workspace.settingValidateCertificates}
checked={settings.validateCertificates}
title="Validate TLS Certificates" title="Validate TLS Certificates"
onChange={(validateCertificates) => onChange={(settingValidateCertificates) =>
updateSettings.mutateAsync({ ...settings, validateCertificates }) updateWorkspace.mutateAsync({ settingValidateCertificates })
} }
/> />
<Checkbox <Checkbox
className="col-span-full" checked={workspace.settingFollowRedirects}
checked={settings.followRedirects}
title="Follow Redirects" title="Follow Redirects"
onChange={(followRedirects) => onChange={(settingFollowRedirects) =>
updateSettings.mutateAsync({ ...settings, followRedirects }) updateWorkspace.mutateAsync({ settingFollowRedirects })
} }
/> />
<div>Request Timeout (ms)</div> <Input
<div> size="xs"
<Input name="requestTimeout"
size="sm" label="Request Timeout (ms)"
name="requestTimeout" labelPosition="left"
label="Request Timeout (ms)" labelClassName="w-1/3"
containerClassName="col-span-2" containerClassName="col-span-2"
hideLabel defaultValue={`${workspace.settingRequestTimeout}`}
defaultValue={`${settings.requestTimeout}`} validate={(value) => parseInt(value) >= 0}
validate={(value) => parseInt(value) >= 0} onChange={(v) => updateWorkspace.mutateAsync({ settingRequestTimeout: parseInt(v) || 0 })}
onChange={(v) => />
updateSettings.mutateAsync({ ...settings, requestTimeout: parseInt(v) || 0 })
}
/>
</div>
<div>Appearance</div> <HStack alignItems="center" space={2}>
<select <div className="w-1/3">Appearance</div>
value={settings.appearance} <select
style={selectBackgroundStyles} value={settings.appearance}
onChange={(e) => updateSettings.mutateAsync({ ...settings, appearance: e.target.value })} style={selectBackgroundStyles}
className={classNames( onChange={(e) =>
'border w-full px-2 outline-none bg-transparent', updateSettings.mutateAsync({ ...settings, appearance: e.target.value })
'border-highlight focus:border-focus', }
'h-sm', className={classNames(
)} 'font-mono text-xs border w-full px-2 outline-none bg-transparent',
> 'border-highlight focus:border-focus',
<option value="system">Match System</option> 'h-xs',
<option value="light">Light</option> )}
<option value="dark">Dark</option> >
</select> <option value="system">Match System</option>
</div> <option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</HStack>
</VStack>
{/*<Checkbox checked={appearance === 'dark'} title="Dark Mode" onChange={toggleAppearance} />*/} {/*<Checkbox checked={appearance === 'dark'} title="Dark Mode" onChange={toggleAppearance} />*/}
</VStack> </VStack>
); );

View File

@@ -17,7 +17,7 @@ export function Checkbox({ checked, onChange, className, disabled, title, hideLa
as="label" as="label"
space={2} space={2}
alignItems="center" alignItems="center"
className={classNames(className, disabled && 'opacity-disabled')} className={classNames(className, 'text-gray-900 text-sm', disabled && 'opacity-disabled')}
> >
<div className="relative flex"> <div className="relative flex">
<input <input

View File

@@ -74,7 +74,7 @@
} }
.cm-scroller { .cm-scroller {
@apply font-mono text-[0.8rem] overflow-hidden; @apply font-mono text-xs overflow-hidden;
} }
.cm-line { .cm-line {

View File

@@ -26,6 +26,7 @@ export type InputProps = Omit<
type?: 'text' | 'password'; type?: 'text' | 'password';
label: string; label: string;
hideLabel?: boolean; hideLabel?: boolean;
labelPosition?: 'top' | 'left';
labelClassName?: string; labelClassName?: string;
containerClassName?: string; containerClassName?: string;
onChange?: (value: string) => void; onChange?: (value: string) => void;
@@ -34,7 +35,7 @@ export type InputProps = Omit<
defaultValue?: string; defaultValue?: string;
leftSlot?: ReactNode; leftSlot?: ReactNode;
rightSlot?: ReactNode; rightSlot?: ReactNode;
size?: 'sm' | 'md' | 'auto'; size?: 'xs' | 'sm' | 'md' | 'auto';
className?: string; className?: string;
placeholder?: string; placeholder?: string;
validate?: (v: string) => boolean; validate?: (v: string) => boolean;
@@ -50,6 +51,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
hideLabel, hideLabel,
label, label,
labelClassName, labelClassName,
labelPosition = 'top',
leftSlot, leftSlot,
name, name,
onBlur, onBlur,
@@ -115,12 +117,19 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
); );
return ( return (
<VStack ref={wrapperRef} className="w-full"> <div
ref={wrapperRef}
className={classNames(
'w-full',
labelPosition === 'left' && 'flex items-center gap-2',
labelPosition === 'top' && 'flex-row gap-0.5',
)}
>
<label <label
htmlFor={id} htmlFor={id}
className={classNames( className={classNames(
labelClassName, labelClassName,
'font-semibold text-xs uppercase text-gray-700', 'text-sm text-gray-900 whitespace-nowrap',
hideLabel && 'sr-only', hideLabel && 'sr-only',
)} )}
> >
@@ -136,6 +145,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
!isValid && '!border-invalid', !isValid && '!border-invalid',
size === 'md' && 'h-md', size === 'md' && 'h-md',
size === 'sm' && 'h-sm', size === 'sm' && 'h-sm',
size === 'xs' && 'h-xs',
size === 'auto' && 'min-h-sm', size === 'auto' && 'min-h-sm',
)} )}
> >
@@ -177,7 +187,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
)} )}
{rightSlot} {rightSlot}
</HStack> </HStack>
</VStack> </div>
); );
}); });

View File

@@ -1,8 +1,8 @@
import type {OsType} from '@tauri-apps/api/os'; import type { OsType } from '@tauri-apps/api/os';
import {useEffect, useRef} from 'react'; import { useEffect, useRef } from 'react';
import {capitalize} from '../lib/capitalize'; import { capitalize } from '../lib/capitalize';
import {debounce} from '../lib/debounce'; import { debounce } from '../lib/debounce';
import {useOsInfo} from './useOsInfo'; import { useOsInfo } from './useOsInfo';
export type HotkeyAction = export type HotkeyAction =
| 'request.send' | 'request.send'
@@ -87,8 +87,6 @@ export function useAnyHotkey(
currentKeys.current.add(normalizeKey(e.key, os)); currentKeys.current.add(normalizeKey(e.key, os));
console.log("HOTKEY", e.key);
for (const [hkAction, hkKeys] of Object.entries(hotkeys) as [HotkeyAction, string[]][]) { for (const [hkAction, hkKeys] of Object.entries(hotkeys) as [HotkeyAction, string[]][]) {
for (const hkKey of hkKeys) { for (const hkKey of hkKeys) {
const keys = hkKey.split('+'); const keys = hkKey.split('+');

View File

@@ -7,7 +7,7 @@ import {
} from '../lib/theme/window'; } from '../lib/theme/window';
import { useSettings } from './useSettings'; import { useSettings } from './useSettings';
export function useTheme() { export function useSyncAppearance() {
const [preferredAppearance, setPreferredAppearance] = useState<Appearance>( const [preferredAppearance, setPreferredAppearance] = useState<Appearance>(
getPreferredAppearance(), getPreferredAppearance(),
); );

View File

@@ -21,6 +21,7 @@ export function useUpdateWorkspace(id: string | null) {
if (workspace === null) return; if (workspace === null) return;
const newWorkspace = typeof v === 'function' ? v(workspace) : { ...workspace, ...v }; const newWorkspace = typeof v === 'function' ? v(workspace) : { ...workspace, ...v };
console.log('NEW WORKSPACE', newWorkspace);
queryClient.setQueryData<Workspace[]>(workspacesQueryKey(workspace), (workspaces) => queryClient.setQueryData<Workspace[]>(workspacesQueryKey(workspace), (workspaces) =>
(workspaces ?? []).map((w) => (w.id === newWorkspace.id ? newWorkspace : w)), (workspaces ?? []).map((w) => (w.id === newWorkspace.id ? newWorkspace : w)),
); );

View File

@@ -19,9 +19,6 @@ export interface BaseModel {
export interface Settings extends BaseModel { export interface Settings extends BaseModel {
readonly model: 'settings'; readonly model: 'settings';
validateCertificates: boolean;
followRedirects: boolean;
requestTimeout: number;
theme: string; theme: string;
appearance: string; appearance: string;
} }
@@ -31,6 +28,9 @@ export interface Workspace extends BaseModel {
name: string; name: string;
description: string; description: string;
variables: EnvironmentVariable[]; variables: EnvironmentVariable[];
settingValidateCertificates: boolean;
settingFollowRedirects: boolean;
settingRequestTimeout: number;
} }
export interface EnvironmentVariable { export interface EnvironmentVariable {

View File

@@ -6,11 +6,7 @@
html, html,
body, body,
#root { #root {
@apply w-full h-full overflow-hidden; @apply w-full h-full overflow-hidden text-gray-900 bg-gray-50;
}
#root {
@apply bg-gray-50 text-gray-900;
} }
/* Setup default transitions for elements */ /* Setup default transitions for elements */