diff --git a/src/components/ResourceEditor/edit-tab-content.tsx b/src/components/ResourceEditor/edit-tab-content.tsx index 9ac0844..06be4c2 100644 --- a/src/components/ResourceEditor/edit-tab-content.tsx +++ b/src/components/ResourceEditor/edit-tab-content.tsx @@ -99,6 +99,7 @@ export function EditTabContent({ issueLineNumbers={issueLineNumbers} getStructureDefinitions={getStructureDefinitions} expandValueSet={expandValueSet} + resourceTypeHint={resourceType} trailingActions={ !isProfileOpen && ( { const vimMode = useVimMode(); return ( @@ -67,6 +69,7 @@ export const EditorTab = ({ getStructureDefinitions={getStructureDefinitions} expandValueSet={expandValueSet} vimMode={vimMode} + resourceTypeHint={resourceTypeHint} /> diff --git a/src/components/rest/collections.tsx b/src/components/rest/collections.tsx index 7bb5ea2..dfac6f2 100644 --- a/src/components/rest/collections.tsx +++ b/src/components/rest/collections.tsx @@ -51,7 +51,10 @@ export async function SaveRequest( saveToRootCollection?: boolean, setLeftMenuOpen?: (open: boolean) => void, ) { - const currentSnippet = collectionEntries.find((entry) => entry.id === tab.id); + const targetId = tab.historyId ?? tab.id; + const currentSnippet = collectionEntries.find( + (entry) => entry.id === targetId, + ); let collection: string | undefined; let snippetId: string; @@ -74,10 +77,17 @@ export async function SaveRequest( snippetId = generateId(); setTabs([ ...tabs.map((t) => ({ ...t, selected: false })), - { ...tab, id: snippetId, selected: true }, + { ...tab, id: snippetId, historyId: snippetId, selected: true }, ]); } else { - snippetId = tab.id; + snippetId = targetId; + // Bind the editor tab to the underlying snippet so subsequent edits + // update the same sidebar entry instead of spawning a new one. + if (tab.historyId !== snippetId) { + setTabs( + tabs.map((t) => (t.id === tab.id ? { ...t, historyId: snippetId } : t)), + ); + } } const result = await client.update({ @@ -399,7 +409,19 @@ function CollectionMoreButton({ tree.getItemInstance(itemId).startRenaming()} + onClick={(e) => { + e.stopPropagation(); + // Defer so the dropdown close completes before input is focused; + // otherwise the immediate blur aborts the rename. Also keep the + // folder expanded — closing it on rename hides children mid-edit. + setTimeout(() => { + const instance = tree.getItemInstance(itemId); + if (instance.isFolder() && !instance.isExpanded()) { + instance.expand(); + } + instance.startRenaming(); + }, 0); + }} > Rename @@ -480,10 +502,13 @@ function SnippetMoreButton({ { - if (itemData.meta?.id !== undefined) { - tree.getItemInstance(itemData.meta.id).startRenaming(); - } + onClick={(e) => { + e.stopPropagation(); + const id = itemData.meta?.id; + if (id === undefined) return; + // Defer so the dropdown close completes before input is focused; + // otherwise the immediate blur aborts the rename. + setTimeout(() => tree.getItemInstance(id).startRenaming(), 0); }} > Rename @@ -639,6 +664,14 @@ function customItemView( className="h-5 border-none p-0" autoFocus {...item.getRenameInputProps()} + onBlur={(e) => { + const tree = item.getTree(); + if (e.target.value.trim()) { + tree.completeRenaming(); + } else { + tree.abortRenaming(); + } + }} /> ) : itemData?.meta?.title ? (
{itemData.meta.title}
@@ -881,6 +914,27 @@ export const CollectionsView = ({ /> ) : (
+ {selectedTab && ( + + )} { + if (item.isFolder()) { + const oldId = item.getId(); + // Carry collapsed state over to the new folder id so the + // rename does not visually expand a previously-closed folder. + if (collapsedItems.includes(oldId)) { + setCollapsedItems( + collapsedItems.map((id) => (id === oldId ? newTitle : id)), + ); + } + } handleRenameSnippet(client, item, newTitle, queryClient); }} onItemLabelClick={(item) => { diff --git a/src/routes/rest.tsx b/src/routes/rest.tsx index 277b9b5..14512bb 100644 --- a/src/routes/rest.tsx +++ b/src/routes/rest.tsx @@ -33,6 +33,7 @@ import { Play, SquareTerminalIcon, Timer, + X, } from "lucide-react"; import type React from "react"; import { @@ -749,6 +750,7 @@ type ResponsePaneProps = { sendVersion: number; onExplainSubTabChange: (subTab: "query" | "statement" | "plan") => void; onFollowContentLocation?: (path: string) => void; + onClearResponse?: () => void; }; function ResponseInfo({ response }: { response: ResponseData }) { @@ -1178,6 +1180,7 @@ function ResponsePane({ sendVersion, onExplainSubTabChange, onFollowContentLocation, + onClearResponse, }: ResponsePaneProps) { // Use response mode from the response itself (set at request time) const responseMode = response?.mode || "json"; @@ -1214,6 +1217,21 @@ function ResponsePane({ onToggle={(state) => onFullScreenToggle(state)} state={fullScreenState} /> + {response && onClearResponse && ( + + + + + Clear response + + )}
@@ -2341,6 +2359,9 @@ function RouteComponent() { aidboxClient={client} sendVersion={sendVersion} onExplainSubTabChange={handleExplainSubTabChange} + onClearResponse={() => + responseStorage.delete(selectedTab.id) + } onFollowContentLocation={(contentPath) => { const updatedTab = { ...selectedTab,