@@ -20,6 +20,7 @@ import {
2020} from '../create-request' ;
2121import { FALLBACK_REQUEST_METHOD , FALLBACK_REQUEST_URL } from '../request-line' ;
2222import { useExplorerStore } from '../use-explorer-store' ;
23+ import { formatJsonBodyText , validateJsonBodyText } from '../utils/json-body' ;
2324import { buildCreateFilePath , toCreateHttpPath } from '../utils/mutations' ;
2425import { parentDirectory } from '../utils/path' ;
2526import {
@@ -282,7 +283,16 @@ export default function ExplorerScreen() {
282283 const request = selectedRequest ( ) ;
283284 const sourceUrl = requestSourceUrl ( ) ;
284285 const content = explorer . fileDraftContent ( ) ;
285- if ( ! request || ! sourceUrl || content === undefined ) {
286+ if ( ! request ) {
287+ setDetailsSaveError ( 'Select a request before saving request details.' ) ;
288+ return ;
289+ }
290+ if ( ! sourceUrl ) {
291+ setDetailsSaveError ( 'Unable to resolve the request URL for this request.' ) ;
292+ return ;
293+ }
294+ if ( content === undefined ) {
295+ setDetailsSaveError ( 'Request file content is still loading. Try saving again.' ) ;
286296 return ;
287297 }
288298
@@ -350,10 +360,26 @@ export default function ExplorerScreen() {
350360
351361 return sourceUrl ;
352362 } ) ;
353- const requestParams = createMemo ( ( ) => draftParams ( ) ) ;
354- const requestHeaders = createMemo ( ( ) => draftHeaders ( ) ) ;
355- const requestBodySummary = createMemo ( ( ) => requestSourceBody ( ) ) ;
356- const requestDiagnostics = createMemo ( ( ) => requestSourceDiagnostics ( ) ) ;
363+ const inlineJsonBodyText = createMemo ( ( ) => {
364+ const body = requestSourceBody ( ) ;
365+ if ( body . kind !== 'inline' || ! body . isJsonLike ) {
366+ return undefined ;
367+ }
368+
369+ if ( isDetailsDirty ( ) ) {
370+ return draftBody ( ) ;
371+ }
372+
373+ return body . text ?? '' ;
374+ } ) ;
375+ const bodyValidationError = createMemo ( ( ) => {
376+ const text = inlineJsonBodyText ( ) ;
377+ if ( text === undefined ) {
378+ return undefined ;
379+ }
380+ return validateJsonBodyText ( text ) ;
381+ } ) ;
382+ const hasSelectedRequest = createMemo ( ( ) => Boolean ( selectedRequest ( ) ) ) ;
357383 const fileDiagnostics = createMemo ( ( ) => parsedRequestFile ( ) ?. diagnostics ?? [ ] ) ;
358384
359385 const requestDetailsError = createMemo ( ( ) => {
@@ -365,7 +391,20 @@ export default function ExplorerScreen() {
365391 const isRequestDetailsLoading = createMemo (
366392 ( ) => Boolean ( parseSource ( ) ) && parsedRequestFile . loading
367393 ) ;
368- const isUnsupportedProtocol = createMemo ( ( ) => ! isHttpProtocol ( selectedRequest ( ) ?. protocol ) ) ;
394+ const isUnsupportedProtocol = createMemo ( ( ) => {
395+ const request = selectedRequest ( ) ;
396+ if ( ! request ) {
397+ return false ;
398+ }
399+ return ! isHttpProtocol ( request . protocol ) ;
400+ } ) ;
401+ const unsupportedProtocolLabel = createMemo ( ( ) => {
402+ const request = selectedRequest ( ) ;
403+ if ( ! request || ! isUnsupportedProtocol ( ) ) {
404+ return undefined ;
405+ }
406+ return request . protocol ?. toUpperCase ( ) ?? 'THIS' ;
407+ } ) ;
369408 const isBusy = createMemo ( ( ) => explorer . isMutating ( ) ) ;
370409 const sendDisabled = createMemo ( ( ) => {
371410 if ( ! selectedPath ( ) || ! selectedRequest ( ) || ! server . client ( ) ) {
@@ -374,6 +413,9 @@ export default function ExplorerScreen() {
374413 if ( isUnsupportedProtocol ( ) ) {
375414 return true ;
376415 }
416+ if ( bodyValidationError ( ) ) {
417+ return true ;
418+ }
377419 return isBusy ( ) || isFileLoading ( ) || isRequestsLoading ( ) || isSavingFile ( ) || isSending ( ) ;
378420 } ) ;
379421 const explorerGridStyle = createMemo < Record < string , string > > ( ( ) => ( {
@@ -512,6 +554,54 @@ export default function ExplorerScreen() {
512554 }
513555 } ;
514556
557+ const prettifyDraftBody = ( ) => {
558+ const body = requestSourceBody ( ) ;
559+ if ( body . kind !== 'inline' || ! body . isJsonLike ) {
560+ return ;
561+ }
562+
563+ const result = formatJsonBodyText ( draftBody ( ) , 'prettify' ) ;
564+ if ( ! result . ok ) {
565+ return ;
566+ }
567+
568+ setDraftBody ( result . text ) ;
569+ markDetailsDirty ( ) ;
570+ } ;
571+
572+ const minifyDraftBody = ( ) => {
573+ const body = requestSourceBody ( ) ;
574+ if ( body . kind !== 'inline' || ! body . isJsonLike ) {
575+ return ;
576+ }
577+
578+ const result = formatJsonBodyText ( draftBody ( ) , 'minify' ) ;
579+ if ( ! result . ok ) {
580+ return ;
581+ }
582+
583+ setDraftBody ( result . text ) ;
584+ markDetailsDirty ( ) ;
585+ } ;
586+
587+ const copyDraftBody = async ( ) => {
588+ if ( ! selectedRequest ( ) ) {
589+ return ;
590+ }
591+
592+ try {
593+ await navigator . clipboard . writeText ( draftBody ( ) ) ;
594+ } catch {
595+ // Ignore clipboard failures and keep editing flow uninterrupted.
596+ }
597+ } ;
598+
599+ const refreshExplorer = ( ) => void explorer . refresh ( ) ;
600+ const submitCreateDialogRequest = ( ) => void submitCreateDialog ( ) ;
601+ const sendSelectedRequestAction = ( ) => void sendSelectedRequest ( ) ;
602+ const copyDraftBodyAction = ( ) => void copyDraftBody ( ) ;
603+ const saveRequestDetailsDraftAction = ( ) => void saveRequestDetailsDraft ( ) ;
604+
515605 return (
516606 < main
517607 class = "flex-1 min-h-0 overflow-hidden grid grid-cols-[var(--explorer-grid-cols)] gap-0 px-2 pt-2 max-[960px]:grid-cols-1 max-[960px]:grid-rows-[var(--explorer-grid-rows-mobile)]"
@@ -524,7 +614,7 @@ export default function ExplorerScreen() {
524614 >
525615 < ExplorerToolbar
526616 onCreate = { openCreateDialog }
527- onRefresh = { ( ) => void explorer . refresh ( ) }
617+ onRefresh = { refreshExplorer }
528618 isRefreshing = { explorer . isLoading ( ) }
529619 isMutating = { isBusy ( ) }
530620 workspaceRoot = { explorer . workspaceRoot ( ) }
@@ -601,7 +691,7 @@ export default function ExplorerScreen() {
601691 onClose = { closeCreateDialog }
602692 onNameChange = { ( value ) => setCreateDialog ( 'name' , value ) }
603693 onKindChange = { ( kind ) => setCreateDialog ( 'kind' , kind ) }
604- onSubmit = { ( ) => void submitCreateDialog ( ) }
694+ onSubmit = { submitCreateDialogRequest }
605695 />
606696
607697 < section
@@ -679,12 +769,12 @@ export default function ExplorerScreen() {
679769 </ div >
680770 </ Show >
681771
682- < Show when = { isUnsupportedProtocol ( ) && selectedRequest ( ) } >
683- < div class = "alert mx-3 mt-3 border border-base-300 bg-base-200/70 text-base-content" >
684- < span class = "text-sm " >
685- { selectedRequest ( ) ?. protocol ?. toUpperCase ( ) } execution wiring is coming next.
686- </ span >
687- </ div >
772+ < Show when = { unsupportedProtocolLabel ( ) } >
773+ { ( protocol ) => (
774+ < div class = "alert mx-3 mt-3 border border-base-300 bg-base-200/70 text-base-content " >
775+ < span class = "text-sm" > { protocol ( ) } execution wiring is coming next.</ span >
776+ </ div >
777+ ) }
688778 </ Show >
689779
690780 < RequestUrlBar
@@ -693,7 +783,7 @@ export default function ExplorerScreen() {
693783 requestOptions = { requestOptions ( ) }
694784 selectedRequestIndex = { selectedRequestIndex ( ) }
695785 onRequestIndexChange = { handleRequestIndexChange }
696- onSend = { ( ) => void sendSelectedRequest ( ) }
786+ onSend = { sendSelectedRequestAction }
697787 disabled = { isBusy ( ) || isFileLoading ( ) || isRequestsLoading ( ) || isSavingFile ( ) }
698788 sendDisabled = { sendDisabled ( ) }
699789 isSending = { isSending ( ) }
@@ -704,12 +794,13 @@ export default function ExplorerScreen() {
704794 style = { requestPanelsStyle ( ) }
705795 >
706796 < RequestDetailsPanel
707- hasRequest = { Boolean ( selectedRequest ( ) ) }
708- params = { requestParams ( ) }
709- headers = { requestHeaders ( ) }
710- bodySummary = { requestBodySummary ( ) }
797+ hasRequest = { hasSelectedRequest ( ) }
798+ params = { draftParams ( ) }
799+ headers = { draftHeaders ( ) }
800+ bodySummary = { requestSourceBody ( ) }
711801 bodyDraft = { draftBody ( ) }
712- diagnostics = { requestDiagnostics ( ) }
802+ bodyValidationError = { bodyValidationError ( ) }
803+ diagnostics = { requestSourceDiagnostics ( ) }
713804 fileDiagnostics = { fileDiagnostics ( ) }
714805 isLoading = { isRequestDetailsLoading ( ) }
715806 error = { requestDetailsError ( ) }
@@ -723,7 +814,10 @@ export default function ExplorerScreen() {
723814 onAddHeader = { addDraftHeader }
724815 onRemoveHeader = { removeDraftHeader }
725816 onBodyChange = { handleDraftBodyChange }
726- onSave = { ( ) => void saveRequestDetailsDraft ( ) }
817+ onBodyPrettify = { prettifyDraftBody }
818+ onBodyMinify = { minifyDraftBody }
819+ onBodyCopy = { copyDraftBodyAction }
820+ onSave = { saveRequestDetailsDraftAction }
727821 onDiscard = { discardRequestDetailsDraft }
728822 />
729823 < Show
0 commit comments