diff --git a/package.json b/package.json index dfe97afa..4f2cab78 100644 --- a/package.json +++ b/package.json @@ -58,8 +58,9 @@ "uuid": "^9.0.0" }, "devDependencies": { + "@vitejs/plugin-react": "^3.1.0", "@tailwindcss/nesting": "^0.0.0-insiders.565cd3e", - "@tauri-apps/cli": "^1.5.6", + "@tauri-apps/cli": "^1.5.4", "@types/node": "^18.7.10", "@types/papaparse": "^5.3.7", "@types/parse-color": "^1.0.1", @@ -69,7 +70,6 @@ "@types/uuid": "^9.0.1", "@typescript-eslint/eslint-plugin": "^5.57.0", "@typescript-eslint/parser": "^5.57.0", - "@vitejs/plugin-react": "^3.1.0", "autoprefixer": "^10.4.13", "eslint": "^8.34.0", "eslint-config-prettier": "^8.6.0", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 11b893a4..052e50d5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -498,17 +498,7 @@ async fn list_environments( .await .expect("Failed to find environments"); - println!(""); - if environments.is_empty() { - println!("CREATING DEFAULT ENVIRONMENT"); - let data: HashMap = HashMap::new(); - let environment = models::create_environment(workspace_id, "Default", data, pool) - .await - .expect("Failed to create default environment"); - Ok(vec![environment]) - } else { - Ok(environments) - } + Ok(environments) } #[tauri::command] diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index ab30f79b..174b7a7c 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -4,7 +4,7 @@ import { keyValueQueryKey } from '../hooks/useKeyValue'; import { requestsQueryKey } from '../hooks/useRequests'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { responsesQueryKey } from '../hooks/useResponses'; -import { useTauriEvent } from '../hooks/useTauriEvent'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { workspacesQueryKey } from '../hooks/useWorkspaces'; import { DEFAULT_FONT_SIZE } from '../lib/constants'; import { NAMESPACE_NO_SYNC } from '../lib/keyValueStore'; @@ -15,7 +15,7 @@ export function GlobalHooks() { const queryClient = useQueryClient(); const { wasUpdatedExternally } = useRequestUpdateKey(null); - useTauriEvent('created_model', ({ payload, windowLabel }) => { + useListenToTauriEvent('created_model', ({ payload, windowLabel }) => { if (shouldIgnoreEvent(payload, windowLabel)) return; const queryKey = @@ -40,7 +40,7 @@ export function GlobalHooks() { } }); - useTauriEvent('updated_model', ({ payload, windowLabel }) => { + useListenToTauriEvent('updated_model', ({ payload, windowLabel }) => { if (shouldIgnoreEvent(payload, windowLabel)) return; const queryKey = @@ -70,7 +70,7 @@ export function GlobalHooks() { } }); - useTauriEvent('deleted_model', ({ payload, windowLabel }) => { + useListenToTauriEvent('deleted_model', ({ payload, windowLabel }) => { if (shouldIgnoreEvent(payload, windowLabel)) return; if (shouldIgnoreModel(payload)) return; @@ -85,7 +85,7 @@ export function GlobalHooks() { queryClient.setQueryData(keyValueQueryKey(payload), undefined); } }); - useTauriEvent('zoom', ({ payload: zoomDelta, windowLabel }) => { + useListenToTauriEvent('zoom', ({ payload: zoomDelta, windowLabel }) => { if (windowLabel !== appWindow.label) return; const fontSize = parseFloat(window.getComputedStyle(document.documentElement).fontSize); diff --git a/src-web/components/RequestActionsDropdown.tsx b/src-web/components/RequestActionsDropdown.tsx index 9a85034c..aab1505b 100644 --- a/src-web/components/RequestActionsDropdown.tsx +++ b/src-web/components/RequestActionsDropdown.tsx @@ -2,12 +2,12 @@ import type { HTMLAttributes, ReactElement } from 'react'; import React, { useRef } from 'react'; import { useDeleteRequest } from '../hooks/useDeleteRequest'; import { useDuplicateRequest } from '../hooks/useDuplicateRequest'; -import { useTauriEvent } from '../hooks/useTauriEvent'; import { useTheme } from '../hooks/useTheme'; import type { DropdownRef } from './core/Dropdown'; import { Dropdown } from './core/Dropdown'; import { HotKey } from './core/HotKey'; import { Icon } from './core/Icon'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; interface Props { requestId: string; @@ -20,12 +20,12 @@ export function RequestActionsDropdown({ requestId, children }: Props) { const dropdownRef = useRef(null); const { appearance, toggleAppearance } = useTheme(); - useTauriEvent('toggle_settings', () => { + useListenToTauriEvent('toggle_settings', () => { dropdownRef.current?.toggle(); }); // TODO: Put this somewhere better - useTauriEvent('duplicate_request', () => { + useListenToTauriEvent('duplicate_request', () => { duplicateRequest.mutate(); }); diff --git a/src-web/components/RequestPane.tsx b/src-web/components/RequestPane.tsx index 11923974..065de782 100644 --- a/src-web/components/RequestPane.tsx +++ b/src-web/components/RequestPane.tsx @@ -6,7 +6,7 @@ import { memo, useCallback, useMemo, useState } from 'react'; import { createGlobalState } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; -import { useTauriEvent } from '../hooks/useTauriEvent'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { useUpdateRequest } from '../hooks/useUpdateRequest'; import { tryFormatJson } from '../lib/formatters'; import type { HttpHeader, HttpRequest } from '../lib/models'; @@ -140,7 +140,7 @@ export const RequestPane = memo(function RequestPane({ style, fullHeight, classN [updateRequest], ); - useTauriEvent( + useListenToTauriEvent( 'send_request', async ({ windowLabel }) => { if (windowLabel !== appWindow.label) return; diff --git a/src-web/components/Sidebar.tsx b/src-web/components/Sidebar.tsx index aa89bdca..a65c569f 100644 --- a/src-web/components/Sidebar.tsx +++ b/src-web/components/Sidebar.tsx @@ -10,16 +10,18 @@ import { useDeleteAnyRequest } from '../hooks/useDeleteAnyRequest'; import { useLatestResponse } from '../hooks/useLatestResponse'; import { useRequests } from '../hooks/useRequests'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; -import { useTauriEvent } from '../hooks/useTauriEvent'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { useUpdateAnyRequest } from '../hooks/useUpdateAnyRequest'; import { useUpdateRequest } from '../hooks/useUpdateRequest'; import type { HttpRequest } from '../lib/models'; import { isResponseLoading } from '../lib/models'; import { Icon } from './core/Icon'; -import { VStack } from './core/Stacks'; +import { HStack, VStack } from './core/Stacks'; import { StatusTag } from './core/StatusTag'; import { DropMarker } from './DropMarker'; import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId'; +import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown'; +import { IconButton } from './core/IconButton'; interface Props { className?: string; @@ -63,7 +65,7 @@ export const Sidebar = memo(function Sidebar({ className }: Props) { routes.navigate('request', { requestId, workspaceId: request.workspaceId, - environmentId: activeEnvironmentId, + environmentId: activeEnvironmentId ?? undefined, }); setSelectedIndex(index); focusActiveRequest(index); @@ -93,7 +95,7 @@ export const Sidebar = memo(function Sidebar({ className }: Props) { useKeyPressEvent('Backspace', handleDeleteKey); useKeyPressEvent('Delete', handleDeleteKey); - useTauriEvent( + useListenToTauriEvent( 'focus_sidebar', () => { if (hidden || hasFocus) return; @@ -149,11 +151,22 @@ export const Sidebar = memo(function Sidebar({ className }: Props) { onFocus={handleFocus} onBlur={handleBlur} tabIndex={hidden ? -1 : 0} - className={classNames(className, 'h-full relative grid grid-rows-[minmax(0,1fr)_auto]')} + className={classNames( + className, + 'h-full relative grid grid-rows-[auto_minmax(0,1fr)_auto]', + )} > + + + + { + useListenToTauriEvent('new_request', () => { createRequest.mutate({}); }); diff --git a/src-web/components/UrlBar.tsx b/src-web/components/UrlBar.tsx index 6a1600ae..d32fc82c 100644 --- a/src-web/components/UrlBar.tsx +++ b/src-web/components/UrlBar.tsx @@ -5,7 +5,7 @@ import { memo, useCallback, useRef } from 'react'; import { useIsResponseLoading } from '../hooks/useIsResponseLoading'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useSendRequest } from '../hooks/useSendRequest'; -import { useTauriEvent } from '../hooks/useTauriEvent'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { useUpdateRequest } from '../hooks/useUpdateRequest'; import type { HttpRequest } from '../lib/models'; import { IconButton } from './core/IconButton'; @@ -39,7 +39,7 @@ export const UrlBar = memo(function UrlBar({ id: requestId, url, method, classNa [sendRequest], ); - useTauriEvent('focus_url', () => { + useListenToTauriEvent('focus_url', () => { inputRef.current?.focus(); }); diff --git a/src-web/components/Workspace.tsx b/src-web/components/Workspace.tsx index 1ef87b52..5d866bbc 100644 --- a/src-web/components/Workspace.tsx +++ b/src-web/components/Workspace.tsx @@ -6,12 +6,12 @@ import type { MouseEvent as ReactMouseEvent, ReactNode, } from 'react'; -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useWindowSize } from 'react-use'; import { useOsInfo } from '../hooks/useOsInfo'; import { useSidebarHidden } from '../hooks/useSidebarHidden'; import { useSidebarWidth } from '../hooks/useSidebarWidth'; -import { useTauriEvent } from '../hooks/useTauriEvent'; +import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { WINDOW_FLOATING_SIDEBAR_WIDTH } from '../lib/constants'; import { Button } from './core/Button'; import { HStack } from './core/Stacks'; @@ -38,7 +38,7 @@ export default function Workspace() { null, ); - useTauriEvent('toggle_sidebar', toggle); + useListenToTauriEvent('toggle_sidebar', toggle); // float/un-float sidebar on window resize useEffect(() => { diff --git a/src-web/components/WorkspaceActionsDropdown.tsx b/src-web/components/WorkspaceActionsDropdown.tsx index 979e7dd2..7fbe818f 100644 --- a/src-web/components/WorkspaceActionsDropdown.tsx +++ b/src-web/components/WorkspaceActionsDropdown.tsx @@ -9,6 +9,7 @@ import { usePrompt } from '../hooks/usePrompt'; import { useUpdateWorkspace } from '../hooks/useUpdateWorkspace'; import { useWorkspaces } from '../hooks/useWorkspaces'; import { Button } from './core/Button'; +import type { ButtonProps } from './core/Button'; import type { DropdownItem } from './core/Dropdown'; import { Dropdown } from './core/Dropdown'; import { Icon } from './core/Icon'; @@ -17,12 +18,11 @@ import { HStack } from './core/Stacks'; import { useDialog } from './DialogContext'; import { useActiveEnvironmentId } from '../hooks/useActiveEnvironmentId'; -type Props = { - className?: string; -}; +type Props = Pick; export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({ className, + ...buttonProps }: Props) { const workspaces = useWorkspaces(); const activeWorkspace = useActiveWorkspace(); @@ -36,9 +36,10 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({ const routes = useAppRoutes(); const items: DropdownItem[] = useMemo(() => { - const workspaceItems = workspaces.map((w) => ({ + const workspaceItems: DropdownItem[] = workspaces.map((w) => ({ key: w.id, label: w.name, + rightSlot: w.id === activeWorkspaceId ? : undefined, onSelect: async () => { dialog.show({ id: 'open-workspace', @@ -147,6 +148,7 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({ ]; }, [ activeWorkspace?.name, + activeWorkspaceId, createWorkspace, deleteWorkspace.mutate, dialog, @@ -163,6 +165,8 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({ size="sm" className={classNames(className, 'text-gray-800 !px-2 truncate')} forDropdown + leftSlot={Workspace logo} + {...buttonProps} > {activeWorkspace?.name} diff --git a/src-web/components/WorkspaceHeader.tsx b/src-web/components/WorkspaceHeader.tsx index 060b0d80..ff0bb85f 100644 --- a/src-web/components/WorkspaceHeader.tsx +++ b/src-web/components/WorkspaceHeader.tsx @@ -6,7 +6,6 @@ import { HStack } from './core/Stacks'; import { RecentRequestsDropdown } from './RecentRequestsDropdown'; import { RequestActionsDropdown } from './RequestActionsDropdown'; import { SidebarActions } from './SidebarActions'; -import { WorkspaceActionsDropdown } from './WorkspaceActionsDropdown'; import { EnvironmentActionsDropdown } from './EnvironmentActionsDropdown'; interface Props { @@ -24,7 +23,6 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop > -
diff --git a/src-web/components/core/Button.tsx b/src-web/components/core/Button.tsx index 7704ed81..5217cb1f 100644 --- a/src-web/components/core/Button.tsx +++ b/src-web/components/core/Button.tsx @@ -23,6 +23,7 @@ export type ButtonProps = HTMLAttributes & { forDropdown?: boolean; disabled?: boolean; title?: string; + leftSlot?: ReactNode; rightSlot?: ReactNode; }; @@ -37,6 +38,7 @@ const _Button = forwardRef(function Button( type = 'button', justify = 'center', size = 'md', + leftSlot, rightSlot, disabled, ...props @@ -63,7 +65,11 @@ const _Button = forwardRef(function Button( return (