fix: cache rendered REST tabs

This commit is contained in:
thibaud-leclere 2026-05-06 10:23:39 +02:00
parent 6dd08e5a88
commit 8e52fafcfa
3 changed files with 116 additions and 19 deletions

View file

@ -0,0 +1,51 @@
import { describe, expect, test } from "vitest"
import { updateRenderedRESTTabIDs } from "../tabRenderCache"
describe("REST tab render cache", () => {
test("keeps the active tab and the 4 most recent rendered tabs", () => {
let renderedTabIDs: string[] = []
for (const activeTabID of ["tab-1", "tab-2", "tab-3", "tab-4", "tab-5"]) {
renderedTabIDs = updateRenderedRESTTabIDs({
renderedTabIDs,
activeTabID,
activeTabIDs: ["tab-1", "tab-2", "tab-3", "tab-4", "tab-5"],
maxRenderedTabs: 5,
})
}
expect(renderedTabIDs).toEqual([
"tab-5",
"tab-4",
"tab-3",
"tab-2",
"tab-1",
])
renderedTabIDs = updateRenderedRESTTabIDs({
renderedTabIDs,
activeTabID: "tab-6",
activeTabIDs: ["tab-1", "tab-2", "tab-3", "tab-4", "tab-5", "tab-6"],
maxRenderedTabs: 5,
})
expect(renderedTabIDs).toEqual([
"tab-6",
"tab-5",
"tab-4",
"tab-3",
"tab-2",
])
})
test("removes closed tabs from the rendered cache", () => {
const renderedTabIDs = updateRenderedRESTTabIDs({
renderedTabIDs: ["tab-5", "tab-4", "tab-3", "tab-2", "tab-1"],
activeTabID: "tab-5",
activeTabIDs: ["tab-1", "tab-3", "tab-5"],
maxRenderedTabs: 5,
})
expect(renderedTabIDs).toEqual(["tab-5", "tab-3", "tab-1"])
})
})

View file

@ -0,0 +1,22 @@
type UpdateRenderedRESTTabIDsOptions = {
renderedTabIDs: string[]
activeTabID: string
activeTabIDs: string[]
maxRenderedTabs: number
}
export function updateRenderedRESTTabIDs({
renderedTabIDs,
activeTabID,
activeTabIDs,
maxRenderedTabs,
}: UpdateRenderedRESTTabIDsOptions) {
const activeTabIDSet = new Set(activeTabIDs)
return [
activeTabID,
...renderedTabIDs.filter((tabID) => tabID !== activeTabID),
]
.filter((tabID) => activeTabIDSet.has(tabID))
.slice(0, maxRenderedTabs)
}

View file

@ -6,6 +6,7 @@
v-if="currentTabID"
:id="'rest_windows'"
v-model="currentTabID"
render-inactive-tabs
@remove-tab="removeTab"
@add-tab="addNewTab"
@sort="sortTabs"
@ -44,24 +45,26 @@
</svg>
</span>
</template>
<HttpExampleResponseTab
v-if="tab.document.type === 'example-response'"
:model-value="tab"
@update:model-value="onTabUpdate"
/>
<!-- Render TabContents -->
<HttpTestRunner
v-if="tab.document.type === 'test-runner'"
:model-value="tab"
@update:model-value="onTabUpdate"
/>
<!-- When document.type === 'request' the tab type is HoppTab<HoppRequestDocument>-->
<HttpRequestTab
v-if="tab.document.type === 'request'"
:model-value="tab"
@update:model-value="onTabUpdate"
/>
<!-- END Render TabContents -->
<template v-if="shouldRenderTabContent(tab.id)">
<HttpExampleResponseTab
v-if="tab.document.type === 'example-response'"
:model-value="tab"
@update:model-value="onTabUpdate"
/>
<!-- Render TabContents -->
<HttpTestRunner
v-if="tab.document.type === 'test-runner'"
:model-value="tab"
@update:model-value="onTabUpdate"
/>
<!-- When document.type === 'request' the tab type is HoppTab<HoppRequestDocument>-->
<HttpRequestTab
v-if="tab.document.type === 'request'"
:model-value="tab"
@update:model-value="onTabUpdate"
/>
<!-- END Render TabContents -->
</template>
</HoppSmartWindow>
<template #actions>
<EnvironmentsSelector class="h-full" />
@ -133,9 +136,10 @@
</template>
<script lang="ts" setup>
import { ref, onMounted, computed } from "vue"
import { ref, onMounted, computed, watch } from "vue"
import { generateUniqueRefId, safelyExtractRESTRequest } from "@hoppscotch/data"
import { translateExtURLParams } from "~/helpers/RESTExtURLParams"
import { updateRenderedRESTTabIDs } from "~/helpers/rest/tabRenderCache"
import { useRoute } from "vue-router"
import { useI18n } from "@composables/i18n"
import { getDefaultRESTRequest } from "~/helpers/rest/default"
@ -164,6 +168,7 @@ const reqName = ref<string>("")
const unsavedTabsCount = ref(0)
const exceptedTabID = ref<string | null>(null)
const renameTabID = ref<string | null>(null)
const MAX_RENDERED_REST_TABS = 5
const t = useI18n()
@ -195,6 +200,25 @@ const contextMenu = ref<PopupDetails>({
})
const activeTabs = tabs.getActiveTabs()
const renderedTabIDs = ref<string[]>([])
const activeTabIDs = computed(() => activeTabs.value.map((tab) => tab.id))
const renderedTabIDSet = computed(() => new Set(renderedTabIDs.value))
watch(
[currentTabID, activeTabIDs],
([activeTabID, activeTabIDs]) => {
renderedTabIDs.value = updateRenderedRESTTabIDs({
renderedTabIDs: renderedTabIDs.value,
activeTabID,
activeTabIDs,
maxRenderedTabs: MAX_RENDERED_REST_TABS,
})
},
{ immediate: true }
)
const shouldRenderTabContent = (tabID: string) =>
renderedTabIDSet.value.has(tabID)
function bindRequestToURLParams() {
const route = useRoute()