diff --git a/agent/package.json b/agent/package.json index 5c914c8..c285e14 100644 --- a/agent/package.json +++ b/agent/package.json @@ -1,6 +1,6 @@ { "name": "@testsmith/api-spector-agent", - "version": "0.5.1", + "version": "0.5.2", "description": "API Spector private runner: monitor internal/local APIs the cloud cannot reach, with no inbound connections.", "license": "MIT", "homepage": "https://api-spector.dev", diff --git a/package-lock.json b/package-lock.json index 237b25b..3374c68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@testsmith/api-spector", - "version": "0.5.1", + "version": "0.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@testsmith/api-spector", - "version": "0.5.1", + "version": "0.5.2", "license": "MIT", "dependencies": { "@codemirror/commands": "^6.10.3", diff --git a/package.json b/package.json index e1dbafd..26c9160 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@testsmith/api-spector", "productName": "API Spector", - "version": "0.5.1", + "version": "0.5.2", "description": "Local-first API testing tool to inspect, test and mock APIs", "repository": { "type": "git", diff --git a/src/main/index.ts b/src/main/index.ts index c009aab..92e6a23 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -86,7 +86,13 @@ function loadAppIcon(): Electron.NativeImage | undefined { } +// Keep the splash on screen at least this long so it reads as intentional, but +// never add a fixed delay on top of load time (it used to always wait 1.2s +// AFTER the window finished loading). +const MIN_SPLASH_MS = 350; + function createWindow(): void { + const startedAt = Date.now(); const splash = createSplashWindow(); const appIcon = loadAppIcon(); @@ -119,11 +125,13 @@ function createWindow(): void { } win.webContents.once('did-finish-load', () => { - // Brief pause so the splash is visible even on fast machines + // Show as soon as the window is loaded; only hold back for whatever remains + // of the minimum splash time (0 once load already took that long). + const remaining = Math.max(0, MIN_SPLASH_MS - (Date.now() - startedAt)); setTimeout(() => { splash.close(); win.show(); - }, 1200); + }, remaining); }); // On Windows the native title bar is shown — include the version in the title diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index f2e0f86..ec1404a 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -208,6 +208,7 @@ export default function App () { const updateMock = useStore( s => s.updateMock ); const theme = useStore( s => s.theme ); const setCommandPaletteOpen = useStore( s => s.setCommandPaletteOpen ); + const requestSend = useStore( s => s.requestSend ); const setWsStatus = useStore( s => s.setWsStatus ); const addWsMessage = useStore( s => s.addWsMessage ); const pushLiveStreamEvents = useStore( s => s.pushLiveStreamEvents ); @@ -283,10 +284,16 @@ export default function App () { e.preventDefault(); setCommandPaletteOpen( true ); } + // Cmd/Ctrl+Enter (re)sends the active request. The active RequestBuilder + // handles the actual send and ignores it for WebSocket/gRPC tabs. + if ( e.key === 'Enter' && ( e.metaKey || e.ctrlKey ) ) { + e.preventDefault(); + requestSend(); + } } window.addEventListener( 'keydown', handleKeyDown ); return () => window.removeEventListener( 'keydown', handleKeyDown ); - }, [setCommandPaletteOpen] ); + }, [setCommandPaletteOpen, requestSend] ); // Keep light class in sync when OS preference changes (system theme) useEffect( () => { diff --git a/src/renderer/src/components/RequestBuilder/BodyTab.tsx b/src/renderer/src/components/RequestBuilder/BodyTab.tsx index 5a64952..86d8347 100644 --- a/src/renderer/src/components/RequestBuilder/BodyTab.tsx +++ b/src/renderer/src/components/RequestBuilder/BodyTab.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2024-2026 Testsmith.io // SPDX-License-Identifier: MIT -import React, { useMemo } from 'react'; +import React, { useMemo, lazy, Suspense } from 'react'; import CodeMirror from '@uiw/react-codemirror'; import { oneDark } from '@codemirror/theme-one-dark'; import type { ApiRequest, RequestBody } from '../../../../shared/types'; @@ -10,8 +10,12 @@ import { varCompletionExtension, varHoverTooltipExtension } from './atCompletion import { jsonWithComments, xmlWithComments, commentKeymap } from './commentKeymap'; import { useVarNames } from '../../hooks/useVarNames'; import { useVarValues } from '../../hooks/useVarValues'; -import { GraphQLEditor } from './GraphQLEditor'; -import { SoapEditor } from './SoapEditor'; + +// GraphQL (graphql + cm6-graphql) and SOAP (WSDL/XML) editors are heavy and only +// used for those body modes, so they load on demand to keep startup lean. +const GraphQLEditor = lazy(() => import('./GraphQLEditor').then(m => ({ default: m.GraphQLEditor }))); +const SoapEditor = lazy(() => import('./SoapEditor').then(m => ({ default: m.SoapEditor }))); +const EditorFallback =
No fields match "{search}"
@@ -181,13 +209,13 @@ function SchemaExplorer({ ) : ( <> {schema.queryType && ( -