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",
"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": {
"columns": [
{
@@ -24,28 +24,33 @@
"type_info": "Datetime"
},
{
"name": "follow_redirects",
"name": "name",
"ordinal": 4,
"type_info": "Bool"
"type_info": "Text"
},
{
"name": "validate_certificates",
"name": "description",
"ordinal": 5,
"type_info": "Bool"
"type_info": "Text"
},
{
"name": "request_timeout",
"name": "setting_request_timeout",
"ordinal": 6,
"type_info": "Int64"
},
{
"name": "theme",
"name": "setting_follow_redirects",
"ordinal": 7,
"type_info": "Text"
"type_info": "Bool"
},
{
"name": "appearance",
"name": "setting_validate_certificates",
"ordinal": 8,
"type_info": "Bool"
},
{
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>",
"ordinal": 9,
"type_info": "Text"
}
],
@@ -61,8 +66,9 @@
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",
"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": {
"columns": [
{
@@ -24,19 +24,14 @@
"type_info": "Datetime"
},
{
"name": "name",
"name": "theme",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "description",
"name": "appearance",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>",
"ordinal": 6,
"type_info": "Text"
}
],
"parameters": {
@@ -48,9 +43,8 @@
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",
"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": {
"columns": [
{
@@ -34,8 +34,23 @@
"type_info": "Text"
},
{
"name": "variables!: sqlx::types::Json<Vec<EnvironmentVariable>>",
"name": "setting_request_timeout",
"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"
}
],
@@ -49,8 +64,11 @@
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};
pub async fn actually_send_request(
pub async fn send_http_request(
request: models::HttpRequest,
response: &models::HttpResponse,
environment_id: &str,
@@ -35,12 +35,8 @@ pub async fn actually_send_request(
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()
.redirect(match settings.follow_redirects {
.redirect(match workspace.setting_follow_redirects {
true => Policy::limited(10), // TODO: Handle redirects natively
false => Policy::none(),
})
@@ -48,13 +44,13 @@ pub async fn actually_send_request(
.brotli(true)
.deflate(true)
.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
.tls_info(true);
if settings.request_timeout > 0 {
if workspace.setting_request_timeout > 0 {
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::plugin::{ImportResources, ImportResult};
use crate::send::actually_send_request;
use crate::http::send_http_request;
use crate::updates::{update_mode_from_str, UpdateMode, YaakUpdater};
mod analytics;
mod models;
mod plugin;
mod render;
mod send;
mod http;
mod updates;
mod window_ext;
mod window_menu;
@@ -84,7 +84,7 @@ async fn send_ephemeral_request(
let response = models::HttpResponse::new();
let environment_id2 = environment_id.unwrap_or("n/a").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]
@@ -199,7 +199,7 @@ async fn send_request(
tokio::spawn(async move {
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)
.await

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,8 @@
import type {OsType} from '@tauri-apps/api/os';
import {useEffect, useRef} from 'react';
import {capitalize} from '../lib/capitalize';
import {debounce} from '../lib/debounce';
import {useOsInfo} from './useOsInfo';
import type { OsType } from '@tauri-apps/api/os';
import { useEffect, useRef } from 'react';
import { capitalize } from '../lib/capitalize';
import { debounce } from '../lib/debounce';
import { useOsInfo } from './useOsInfo';
export type HotkeyAction =
| 'request.send'
@@ -87,8 +87,6 @@ export function useAnyHotkey(
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 hkKey of hkKeys) {
const keys = hkKey.split('+');

View File

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

View File

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

View File

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

View File

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