@scalar/api-client 3.21.1 → 3.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/components/SectionFilter.vue.d.ts +0 -2
- package/dist/components/SectionFilter.vue.d.ts.map +1 -1
- package/dist/components/SectionFilter.vue.js +1 -1
- package/dist/components/SectionFilter.vue.js.map +1 -1
- package/dist/components/SectionFilter.vue.script.js +2 -8
- package/dist/components/SectionFilter.vue.script.js.map +1 -1
- package/dist/components/SectionFilterButton.vue.d.ts +0 -1
- package/dist/components/SectionFilterButton.vue.d.ts.map +1 -1
- package/dist/components/SectionFilterButton.vue.js.map +1 -1
- package/dist/components/SectionFilterButton.vue.script.js +2 -10
- package/dist/components/SectionFilterButton.vue.script.js.map +1 -1
- package/dist/style.css +146 -81
- package/dist/v2/blocks/operation-block/components/Header.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.js +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js +2 -0
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.js +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.js.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js +17 -6
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js +2 -0
- package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js.map +1 -1
- package/dist/v2/blocks/response-block/ResponseBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/response-block/ResponseBlock.vue.js +1 -1
- package/dist/v2/blocks/response-block/ResponseBlock.vue.js.map +1 -1
- package/dist/v2/blocks/response-block/ResponseBlock.vue.script.js +0 -2
- package/dist/v2/blocks/response-block/ResponseBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js +23 -43
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.d.ts +7 -0
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js +6 -0
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.script.js +5 -5
- package/dist/v2/blocks/scalar-auth-selector-block/components/OAuth2.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.script.js +11 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/RequestAuthDataTableInput.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.js +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/helpers/security-scheme.js.map +1 -1
- package/dist/v2/components/code-input/CodeInputLite.vue.d.ts.map +1 -1
- package/dist/v2/components/code-input/CodeInputLite.vue.js +1 -1
- package/dist/v2/components/code-input/CodeInputLite.vue.js.map +1 -1
- package/dist/v2/components/code-input/CodeInputLite.vue.script.js +13 -1
- package/dist/v2/components/code-input/CodeInputLite.vue.script.js.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.d.ts.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.js +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.js.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.script.js +7 -2
- package/dist/v2/components/data-table/DataTableInput.vue.script.js.map +1 -1
- package/dist/v2/components/layout/CollapsibleSection.vue.d.ts +20 -0
- package/dist/v2/components/layout/CollapsibleSection.vue.d.ts.map +1 -1
- package/dist/v2/components/layout/CollapsibleSection.vue.js.map +1 -1
- package/dist/v2/components/layout/CollapsibleSection.vue.script.js +36 -16
- package/dist/v2/components/layout/CollapsibleSection.vue.script.js.map +1 -1
- package/dist/v2/components/layout/collapsible-section-heading-level.d.ts +13 -0
- package/dist/v2/components/layout/collapsible-section-heading-level.d.ts.map +1 -0
- package/dist/v2/components/layout/collapsible-section-heading-level.js +14 -0
- package/dist/v2/components/layout/collapsible-section-heading-level.js.map +1 -0
- package/dist/v2/components/layout/index.d.ts +1 -0
- package/dist/v2/components/layout/index.d.ts.map +1 -1
- package/dist/v2/components/layout/index.js +2 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.d.ts.map +1 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.js +1 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.js.map +1 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.script.js +10 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.script.js.map +1 -1
- package/dist/v2/components/sidebar/SidebarToggle.vue.d.ts +15 -2
- package/dist/v2/components/sidebar/SidebarToggle.vue.d.ts.map +1 -1
- package/dist/v2/components/sidebar/SidebarToggle.vue.js +3 -1
- package/dist/v2/components/sidebar/SidebarToggle.vue.js.map +1 -1
- package/dist/v2/components/sidebar/SidebarToggle.vue.script.js +5 -5
- package/dist/v2/components/sidebar/SidebarToggle.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/modal/Modal.vue.d.ts.map +1 -1
- package/dist/v2/features/modal/Modal.vue.js.map +1 -1
- package/dist/v2/features/modal/Modal.vue.script.js +2 -1
- package/dist/v2/features/modal/Modal.vue.script.js.map +1 -1
- package/dist/vue-styles.css +114 -73
- package/package.json +12 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResponseBlock.vue.js","names":[],"sources":["../../../../src/v2/blocks/response-block/ResponseBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { RequestPayload } from '@scalar/workspace-store/request-example'\nimport { computed, ref, useId } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ResponseInstance } from '@/v2/blocks/operation-block/helpers/send-request'\nimport HeadersComponent from '@/v2/blocks/response-block/components/Headers.vue'\nimport ResponseBody from '@/v2/blocks/response-block/components/ResponseBody.vue'\nimport ResponseBodyStreaming from '@/v2/blocks/response-block/components/ResponseBodyStreaming.vue'\nimport ResponseBodyVirtual from '@/v2/blocks/response-block/components/ResponseBodyVirtual.vue'\nimport ResponseCookies from '@/v2/blocks/response-block/components/ResponseCookies.vue'\nimport ResponseEmpty from '@/v2/blocks/response-block/components/ResponseEmpty.vue'\nimport ResponseLoadingOverlay from '@/v2/blocks/response-block/components/ResponseLoadingOverlay.vue'\nimport ResponseMetaInformation from '@/v2/blocks/response-block/components/ResponseMetaInformation.vue'\nimport { textMediaTypes } from '@/v2/blocks/response-block/helpers/media-types'\nimport { parseSetCookie } from '@/v2/blocks/response-block/helpers/parse-set-cookie'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nconst { layout, totalPerformedRequests, response, requestPayload } =\n defineProps<{\n /** Wait for the selected external request example before sending. */\n executionDisabled?: boolean\n /** Preprocessed response */\n response: ResponseInstance | null\n /** Original request as a [url, RequestInit] tuple */\n requestPayload: RequestPayload | null\n /** Client layout */\n layout: ClientLayout\n /** Total number of performed requests */\n totalPerformedRequests: number\n /** Application version */\n appVersion: string\n /** Registered app plugins */\n plugins: ClientPlugin[]\n /** Workspace event bus */\n eventBus: WorkspaceEventBus\n }>()\n\nconst { translate } = useLocalization()\n\n// Headers\nconst responseHeaders = computed(() => {\n const headers = response?.headers\n\n return headers\n ? Object.keys(headers).map((key) => ({\n name: key,\n value: headers[key] ?? '',\n }))\n : []\n})\n\nconst responseContentType = computed(\n () => response?.headers['content-type'] ?? response?.headers['Content-Type'],\n)\n\n// Cookies\nconst responseCookies = computed(\n () =>\n response?.cookieHeaderKeys\n .map((setCookieValue) => parseSetCookie(setCookieValue))\n .filter(isDefined) ?? [],\n)\n\nconst responseSections = ['Cookies', 'Headers', 'Body'] as const\ntype Filter = 'All' | (typeof responseSections)[number]\nconst activeFilter = ref<Filter>('All')\n\nconst filters = computed<Filter[]>(() => ['All', ...responseSections])\n\nconst filterIds = computed<Record<Filter, string>>(() => ({\n All: useId(),\n Cookies: useId(),\n Headers: useId(),\n Body: useId(),\n}))\n\n/** Threshold for virtualizing response bodies in bytes */\nconst VIRTUALIZATION_THRESHOLD = 200_000\nconst shouldVirtualize = computed(() => {\n if (!response || !('size' in response)) {\n return false\n }\n\n // Get content type from headers\n const contentType =\n response.headers?.['content-type'] || response.headers?.['Content-Type']\n\n // If no content type or response size is small, don't virtualize\n if (!contentType || (response.size ?? 0) <= VIRTUALIZATION_THRESHOLD) {\n return false\n }\n\n // Do not virtualize html\n if (contentType.includes('text/html')) {\n return false\n }\n\n // Check if content type matches any text-based type\n const isTextBased = textMediaTypes.some((type) => contentType.includes(type))\n\n return isTextBased && (response.size ?? 0) > VIRTUALIZATION_THRESHOLD\n})\n\nconst requestHeaders = computed(() => {\n const headers = requestPayload?.[1]?.headers\n if (!headers) {\n return []\n }\n\n // Normalise via the Headers constructor, and then spread\n return [...new Headers(headers)].map(([name, value]) => ({\n name,\n value,\n required: false,\n }))\n})\n\nconst isSectionVisible = (\n section: (typeof responseSections)[number] | 'All',\n) => {\n if (activeFilter.value === 'All' || activeFilter.value === section) {\n return true\n }\n return false\n}\n\ndefineExpose({\n responseHeaders,\n responseCookies,\n requestHeaders,\n shouldVirtualize,\n activeFilter,\n filters,\n})\n\nconst filterLabels = computed(() => ({\n All: translate('apiClient.sectionFilter.all'),\n Auth: translate('apiClient.sectionFilter.auth'),\n Variables: translate('apiClient.sectionFilter.variables'),\n Cookies: translate('apiClient.sectionFilter.cookies'),\n Headers: translate('apiClient.sectionFilter.headers'),\n Query: translate('apiClient.sectionFilter.query'),\n Body: translate('apiClient.sectionFilter.body'),\n}))\n</script>\n<template>\n <!-- Bound the stacked response section so long bodies keep their own scroll area. -->\n <ViewLayoutSection\n :aria-label=\"translate('apiClient.responseBlock.response')\"\n class=\"max-xl:max-h-full\">\n <template #title>\n <div class=\"flex h-8 flex-1 items-center\">\n <div\n aria-live=\"polite\"\n class=\"flex items-center\"\n :class=\"{ 'animate-response-heading': response }\">\n <span class=\"response-heading pointer-events-none absolute\">{{\n translate('apiClient.responseBlock.response')\n }}</span>\n <ResponseMetaInformation\n v-if=\"response\"\n class=\"animate-response-children\"\n :eventBus=\"eventBus\"\n :response=\"response\" />\n </div>\n <SectionFilter\n v-model=\"activeFilter\"\n :filterIds=\"filterIds\"\n :filters=\"filters\"\n :labels=\"filterLabels\" />\n </div>\n </template>\n <div\n :id=\"filterIds.All\"\n class=\"custom-scroll response-section-content relative grid h-full justify-stretch\"\n :class=\"{\n 'content-start': response,\n }\"\n :role=\"activeFilter === 'All' && response ? 'tabpanel' : 'none'\">\n <template v-if=\"!response\">\n <ResponseEmpty\n :appVersion=\"appVersion\"\n :executionDisabled\n :layout=\"layout\"\n :totalPerformedRequests=\"totalPerformedRequests\"\n @addRequest=\"\n eventBus.emit('ui:open:command-palette', {\n action: 'create-request',\n payload: undefined,\n })\n \"\n @openCommandPalette=\"eventBus.emit('ui:open:command-palette')\"\n @sendRequest=\"eventBus.emit('operation:send:request:hotkey')\" />\n </template>\n <template v-else>\n <!-- Cookies section -->\n <ResponseCookies\n v-if=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n class=\"response-section-content-cookies\"\n :cookies=\"responseCookies\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n <!-- Request headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"requestHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.requestHeaders') }}\n </template>\n </HeadersComponent>\n <!-- Response headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.responseHeaders') }}\n </template>\n </HeadersComponent>\n\n <!-- Inject response section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.response.component\"\n v-if=\"plugin?.components?.response\"\n v-show=\"activeFilter === 'All'\"\n v-bind=\"plugin.components.response.additionalProps\" />\n </ScalarErrorBoundary>\n\n <template v-if=\"activeFilter === 'All' || activeFilter === 'Body'\">\n <!-- Streaming response body -->\n <ResponseBodyStreaming\n v-if=\"'reader' in response\"\n :id=\"filterIds.Body\"\n class=\"response-section-content-body\"\n :contentType=\"responseContentType\"\n :reader=\"response.reader\" />\n\n <!-- Virtualized Text for massive responses -->\n <ResponseBodyVirtual\n v-else-if=\"shouldVirtualize && typeof response?.data === 'string'\"\n :id=\"filterIds.Body\"\n :content=\"response!.data\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n\n <!-- Regular response body -->\n <ResponseBody\n v-else\n :id=\"filterIds.Body\"\n :active=\"true\"\n class=\"response-section-content-body\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n layout=\"client\"\n :plugins=\"plugins\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\"\n :title=\"translate('apiClient.responseBlock.body')\" />\n </template>\n </template>\n <ResponseLoadingOverlay :eventBus=\"eventBus\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.animate-response-heading .response-heading {\n animation: push-response 0.2s ease-in-out forwards;\n opacity: 1;\n}\n@keyframes push-response {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(-4px);\n }\n}\n.animate-response-heading .animate-response-children {\n animation: response-spans 0.2s ease-in-out forwards 0.05s;\n opacity: 0;\n}\n\n@keyframes response-spans {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"ResponseBlock.vue.js","names":[],"sources":["../../../../src/v2/blocks/response-block/ResponseBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { RequestPayload } from '@scalar/workspace-store/request-example'\nimport { computed, ref, useId } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ResponseInstance } from '@/v2/blocks/operation-block/helpers/send-request'\nimport HeadersComponent from '@/v2/blocks/response-block/components/Headers.vue'\nimport ResponseBody from '@/v2/blocks/response-block/components/ResponseBody.vue'\nimport ResponseBodyStreaming from '@/v2/blocks/response-block/components/ResponseBodyStreaming.vue'\nimport ResponseBodyVirtual from '@/v2/blocks/response-block/components/ResponseBodyVirtual.vue'\nimport ResponseCookies from '@/v2/blocks/response-block/components/ResponseCookies.vue'\nimport ResponseEmpty from '@/v2/blocks/response-block/components/ResponseEmpty.vue'\nimport ResponseLoadingOverlay from '@/v2/blocks/response-block/components/ResponseLoadingOverlay.vue'\nimport ResponseMetaInformation from '@/v2/blocks/response-block/components/ResponseMetaInformation.vue'\nimport { textMediaTypes } from '@/v2/blocks/response-block/helpers/media-types'\nimport { parseSetCookie } from '@/v2/blocks/response-block/helpers/parse-set-cookie'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nconst { layout, totalPerformedRequests, response, requestPayload } =\n defineProps<{\n /** Wait for the selected external request example before sending. */\n executionDisabled?: boolean\n /** Preprocessed response */\n response: ResponseInstance | null\n /** Original request as a [url, RequestInit] tuple */\n requestPayload: RequestPayload | null\n /** Client layout */\n layout: ClientLayout\n /** Total number of performed requests */\n totalPerformedRequests: number\n /** Application version */\n appVersion: string\n /** Registered app plugins */\n plugins: ClientPlugin[]\n /** Workspace event bus */\n eventBus: WorkspaceEventBus\n }>()\n\nconst { translate } = useLocalization()\n\n// Headers\nconst responseHeaders = computed(() => {\n const headers = response?.headers\n\n return headers\n ? Object.keys(headers).map((key) => ({\n name: key,\n value: headers[key] ?? '',\n }))\n : []\n})\n\nconst responseContentType = computed(\n () => response?.headers['content-type'] ?? response?.headers['Content-Type'],\n)\n\n// Cookies\nconst responseCookies = computed(\n () =>\n response?.cookieHeaderKeys\n .map((setCookieValue) => parseSetCookie(setCookieValue))\n .filter(isDefined) ?? [],\n)\n\nconst responseSections = ['Cookies', 'Headers', 'Body'] as const\ntype Filter = 'All' | (typeof responseSections)[number]\nconst activeFilter = ref<Filter>('All')\n\nconst filters = computed<Filter[]>(() => ['All', ...responseSections])\n\nconst filterIds = computed<Record<Filter, string>>(() => ({\n All: useId(),\n Cookies: useId(),\n Headers: useId(),\n Body: useId(),\n}))\n\n/** Threshold for virtualizing response bodies in bytes */\nconst VIRTUALIZATION_THRESHOLD = 200_000\nconst shouldVirtualize = computed(() => {\n if (!response || !('size' in response)) {\n return false\n }\n\n // Get content type from headers\n const contentType =\n response.headers?.['content-type'] || response.headers?.['Content-Type']\n\n // If no content type or response size is small, don't virtualize\n if (!contentType || (response.size ?? 0) <= VIRTUALIZATION_THRESHOLD) {\n return false\n }\n\n // Do not virtualize html\n if (contentType.includes('text/html')) {\n return false\n }\n\n // Check if content type matches any text-based type\n const isTextBased = textMediaTypes.some((type) => contentType.includes(type))\n\n return isTextBased && (response.size ?? 0) > VIRTUALIZATION_THRESHOLD\n})\n\nconst requestHeaders = computed(() => {\n const headers = requestPayload?.[1]?.headers\n if (!headers) {\n return []\n }\n\n // Normalise via the Headers constructor, and then spread\n return [...new Headers(headers)].map(([name, value]) => ({\n name,\n value,\n required: false,\n }))\n})\n\nconst isSectionVisible = (\n section: (typeof responseSections)[number] | 'All',\n) => {\n if (activeFilter.value === 'All' || activeFilter.value === section) {\n return true\n }\n return false\n}\n\ndefineExpose({\n responseHeaders,\n responseCookies,\n requestHeaders,\n shouldVirtualize,\n activeFilter,\n filters,\n})\n\nconst filterLabels = computed(() => ({\n All: translate('apiClient.sectionFilter.all'),\n Auth: translate('apiClient.sectionFilter.auth'),\n Variables: translate('apiClient.sectionFilter.variables'),\n Cookies: translate('apiClient.sectionFilter.cookies'),\n Headers: translate('apiClient.sectionFilter.headers'),\n Query: translate('apiClient.sectionFilter.query'),\n Body: translate('apiClient.sectionFilter.body'),\n}))\n</script>\n<template>\n <!-- Bound the stacked response section so long bodies keep their own scroll area. -->\n <ViewLayoutSection\n :aria-label=\"translate('apiClient.responseBlock.response')\"\n class=\"max-xl:max-h-full\">\n <template #title>\n <div class=\"flex h-8 flex-1 items-center\">\n <div\n aria-live=\"polite\"\n class=\"flex items-center\"\n :class=\"{ 'animate-response-heading': response }\">\n <span class=\"response-heading pointer-events-none absolute\">{{\n translate('apiClient.responseBlock.response')\n }}</span>\n <ResponseMetaInformation\n v-if=\"response\"\n class=\"animate-response-children\"\n :eventBus=\"eventBus\"\n :response=\"response\" />\n </div>\n <SectionFilter\n v-model=\"activeFilter\"\n :filters=\"filters\"\n :labels=\"filterLabels\" />\n </div>\n </template>\n <div\n :id=\"filterIds.All\"\n class=\"custom-scroll response-section-content relative grid h-full justify-stretch\"\n :class=\"{\n 'content-start': response,\n }\"\n :role=\"activeFilter === 'All' && response ? 'tabpanel' : 'none'\">\n <template v-if=\"!response\">\n <ResponseEmpty\n :appVersion=\"appVersion\"\n :executionDisabled\n :layout=\"layout\"\n :totalPerformedRequests=\"totalPerformedRequests\"\n @addRequest=\"\n eventBus.emit('ui:open:command-palette', {\n action: 'create-request',\n payload: undefined,\n })\n \"\n @openCommandPalette=\"eventBus.emit('ui:open:command-palette')\"\n @sendRequest=\"eventBus.emit('operation:send:request:hotkey')\" />\n </template>\n <template v-else>\n <!-- Cookies section -->\n <ResponseCookies\n v-if=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n class=\"response-section-content-cookies\"\n :cookies=\"responseCookies\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n <!-- Request headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"requestHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.requestHeaders') }}\n </template>\n </HeadersComponent>\n <!-- Response headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.responseHeaders') }}\n </template>\n </HeadersComponent>\n\n <!-- Inject response section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.response.component\"\n v-if=\"plugin?.components?.response\"\n v-show=\"activeFilter === 'All'\"\n v-bind=\"plugin.components.response.additionalProps\" />\n </ScalarErrorBoundary>\n\n <template v-if=\"activeFilter === 'All' || activeFilter === 'Body'\">\n <!-- Streaming response body -->\n <ResponseBodyStreaming\n v-if=\"'reader' in response\"\n :id=\"filterIds.Body\"\n class=\"response-section-content-body\"\n :contentType=\"responseContentType\"\n :reader=\"response.reader\" />\n\n <!-- Virtualized Text for massive responses -->\n <ResponseBodyVirtual\n v-else-if=\"shouldVirtualize && typeof response?.data === 'string'\"\n :id=\"filterIds.Body\"\n :content=\"response!.data\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n\n <!-- Regular response body -->\n <ResponseBody\n v-else\n :id=\"filterIds.Body\"\n :active=\"true\"\n class=\"response-section-content-body\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n layout=\"client\"\n :plugins=\"plugins\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\"\n :title=\"translate('apiClient.responseBlock.body')\" />\n </template>\n </template>\n <ResponseLoadingOverlay :eventBus=\"eventBus\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.animate-response-heading .response-heading {\n animation: push-response 0.2s ease-in-out forwards;\n opacity: 1;\n}\n@keyframes push-response {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(-4px);\n }\n}\n.animate-response-heading .animate-response-children {\n animation: response-spans 0.2s ease-in-out forwards 0.05s;\n opacity: 0;\n}\n\n@keyframes response-spans {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -109,12 +109,10 @@ var ResponseBlock_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ de
|
|
|
109
109
|
}, null, 8, ["eventBus", "response"])) : createCommentVNode("", true)], 2), createVNode(SectionFilter_default, {
|
|
110
110
|
modelValue: activeFilter.value,
|
|
111
111
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => activeFilter.value = $event),
|
|
112
|
-
filterIds: filterIds.value,
|
|
113
112
|
filters: filters.value,
|
|
114
113
|
labels: filterLabels.value
|
|
115
114
|
}, null, 8, [
|
|
116
115
|
"modelValue",
|
|
117
|
-
"filterIds",
|
|
118
116
|
"filters",
|
|
119
117
|
"labels"
|
|
120
118
|
])])]),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResponseBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/response-block/ResponseBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { RequestPayload } from '@scalar/workspace-store/request-example'\nimport { computed, ref, useId } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ResponseInstance } from '@/v2/blocks/operation-block/helpers/send-request'\nimport HeadersComponent from '@/v2/blocks/response-block/components/Headers.vue'\nimport ResponseBody from '@/v2/blocks/response-block/components/ResponseBody.vue'\nimport ResponseBodyStreaming from '@/v2/blocks/response-block/components/ResponseBodyStreaming.vue'\nimport ResponseBodyVirtual from '@/v2/blocks/response-block/components/ResponseBodyVirtual.vue'\nimport ResponseCookies from '@/v2/blocks/response-block/components/ResponseCookies.vue'\nimport ResponseEmpty from '@/v2/blocks/response-block/components/ResponseEmpty.vue'\nimport ResponseLoadingOverlay from '@/v2/blocks/response-block/components/ResponseLoadingOverlay.vue'\nimport ResponseMetaInformation from '@/v2/blocks/response-block/components/ResponseMetaInformation.vue'\nimport { textMediaTypes } from '@/v2/blocks/response-block/helpers/media-types'\nimport { parseSetCookie } from '@/v2/blocks/response-block/helpers/parse-set-cookie'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nconst { layout, totalPerformedRequests, response, requestPayload } =\n defineProps<{\n /** Wait for the selected external request example before sending. */\n executionDisabled?: boolean\n /** Preprocessed response */\n response: ResponseInstance | null\n /** Original request as a [url, RequestInit] tuple */\n requestPayload: RequestPayload | null\n /** Client layout */\n layout: ClientLayout\n /** Total number of performed requests */\n totalPerformedRequests: number\n /** Application version */\n appVersion: string\n /** Registered app plugins */\n plugins: ClientPlugin[]\n /** Workspace event bus */\n eventBus: WorkspaceEventBus\n }>()\n\nconst { translate } = useLocalization()\n\n// Headers\nconst responseHeaders = computed(() => {\n const headers = response?.headers\n\n return headers\n ? Object.keys(headers).map((key) => ({\n name: key,\n value: headers[key] ?? '',\n }))\n : []\n})\n\nconst responseContentType = computed(\n () => response?.headers['content-type'] ?? response?.headers['Content-Type'],\n)\n\n// Cookies\nconst responseCookies = computed(\n () =>\n response?.cookieHeaderKeys\n .map((setCookieValue) => parseSetCookie(setCookieValue))\n .filter(isDefined) ?? [],\n)\n\nconst responseSections = ['Cookies', 'Headers', 'Body'] as const\ntype Filter = 'All' | (typeof responseSections)[number]\nconst activeFilter = ref<Filter>('All')\n\nconst filters = computed<Filter[]>(() => ['All', ...responseSections])\n\nconst filterIds = computed<Record<Filter, string>>(() => ({\n All: useId(),\n Cookies: useId(),\n Headers: useId(),\n Body: useId(),\n}))\n\n/** Threshold for virtualizing response bodies in bytes */\nconst VIRTUALIZATION_THRESHOLD = 200_000\nconst shouldVirtualize = computed(() => {\n if (!response || !('size' in response)) {\n return false\n }\n\n // Get content type from headers\n const contentType =\n response.headers?.['content-type'] || response.headers?.['Content-Type']\n\n // If no content type or response size is small, don't virtualize\n if (!contentType || (response.size ?? 0) <= VIRTUALIZATION_THRESHOLD) {\n return false\n }\n\n // Do not virtualize html\n if (contentType.includes('text/html')) {\n return false\n }\n\n // Check if content type matches any text-based type\n const isTextBased = textMediaTypes.some((type) => contentType.includes(type))\n\n return isTextBased && (response.size ?? 0) > VIRTUALIZATION_THRESHOLD\n})\n\nconst requestHeaders = computed(() => {\n const headers = requestPayload?.[1]?.headers\n if (!headers) {\n return []\n }\n\n // Normalise via the Headers constructor, and then spread\n return [...new Headers(headers)].map(([name, value]) => ({\n name,\n value,\n required: false,\n }))\n})\n\nconst isSectionVisible = (\n section: (typeof responseSections)[number] | 'All',\n) => {\n if (activeFilter.value === 'All' || activeFilter.value === section) {\n return true\n }\n return false\n}\n\ndefineExpose({\n responseHeaders,\n responseCookies,\n requestHeaders,\n shouldVirtualize,\n activeFilter,\n filters,\n})\n\nconst filterLabels = computed(() => ({\n All: translate('apiClient.sectionFilter.all'),\n Auth: translate('apiClient.sectionFilter.auth'),\n Variables: translate('apiClient.sectionFilter.variables'),\n Cookies: translate('apiClient.sectionFilter.cookies'),\n Headers: translate('apiClient.sectionFilter.headers'),\n Query: translate('apiClient.sectionFilter.query'),\n Body: translate('apiClient.sectionFilter.body'),\n}))\n</script>\n<template>\n <!-- Bound the stacked response section so long bodies keep their own scroll area. -->\n <ViewLayoutSection\n :aria-label=\"translate('apiClient.responseBlock.response')\"\n class=\"max-xl:max-h-full\">\n <template #title>\n <div class=\"flex h-8 flex-1 items-center\">\n <div\n aria-live=\"polite\"\n class=\"flex items-center\"\n :class=\"{ 'animate-response-heading': response }\">\n <span class=\"response-heading pointer-events-none absolute\">{{\n translate('apiClient.responseBlock.response')\n }}</span>\n <ResponseMetaInformation\n v-if=\"response\"\n class=\"animate-response-children\"\n :eventBus=\"eventBus\"\n :response=\"response\" />\n </div>\n <SectionFilter\n v-model=\"activeFilter\"\n :filterIds=\"filterIds\"\n :filters=\"filters\"\n :labels=\"filterLabels\" />\n </div>\n </template>\n <div\n :id=\"filterIds.All\"\n class=\"custom-scroll response-section-content relative grid h-full justify-stretch\"\n :class=\"{\n 'content-start': response,\n }\"\n :role=\"activeFilter === 'All' && response ? 'tabpanel' : 'none'\">\n <template v-if=\"!response\">\n <ResponseEmpty\n :appVersion=\"appVersion\"\n :executionDisabled\n :layout=\"layout\"\n :totalPerformedRequests=\"totalPerformedRequests\"\n @addRequest=\"\n eventBus.emit('ui:open:command-palette', {\n action: 'create-request',\n payload: undefined,\n })\n \"\n @openCommandPalette=\"eventBus.emit('ui:open:command-palette')\"\n @sendRequest=\"eventBus.emit('operation:send:request:hotkey')\" />\n </template>\n <template v-else>\n <!-- Cookies section -->\n <ResponseCookies\n v-if=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n class=\"response-section-content-cookies\"\n :cookies=\"responseCookies\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n <!-- Request headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"requestHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.requestHeaders') }}\n </template>\n </HeadersComponent>\n <!-- Response headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.responseHeaders') }}\n </template>\n </HeadersComponent>\n\n <!-- Inject response section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.response.component\"\n v-if=\"plugin?.components?.response\"\n v-show=\"activeFilter === 'All'\"\n v-bind=\"plugin.components.response.additionalProps\" />\n </ScalarErrorBoundary>\n\n <template v-if=\"activeFilter === 'All' || activeFilter === 'Body'\">\n <!-- Streaming response body -->\n <ResponseBodyStreaming\n v-if=\"'reader' in response\"\n :id=\"filterIds.Body\"\n class=\"response-section-content-body\"\n :contentType=\"responseContentType\"\n :reader=\"response.reader\" />\n\n <!-- Virtualized Text for massive responses -->\n <ResponseBodyVirtual\n v-else-if=\"shouldVirtualize && typeof response?.data === 'string'\"\n :id=\"filterIds.Body\"\n :content=\"response!.data\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n\n <!-- Regular response body -->\n <ResponseBody\n v-else\n :id=\"filterIds.Body\"\n :active=\"true\"\n class=\"response-section-content-body\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n layout=\"client\"\n :plugins=\"plugins\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\"\n :title=\"translate('apiClient.responseBlock.body')\" />\n </template>\n </template>\n <ResponseLoadingOverlay :eventBus=\"eventBus\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.animate-response-heading .response-heading {\n animation: push-response 0.2s ease-in-out forwards;\n opacity: 1;\n}\n@keyframes push-response {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(-4px);\n }\n}\n.animate-response-heading .animate-response-children {\n animation: response-spans 0.2s ease-in-out forwards 0.05s;\n opacity: 0;\n}\n\n@keyframes response-spans {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ResponseBlock.vue.script.js","names":[],"sources":["../../../../src/v2/blocks/response-block/ResponseBlock.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { isDefined } from '@scalar/helpers/array/is-defined'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { RequestPayload } from '@scalar/workspace-store/request-example'\nimport { computed, ref, useId } from 'vue'\n\nimport SectionFilter from '@/components/SectionFilter.vue'\nimport ViewLayoutSection from '@/components/ViewLayout/ViewLayoutSection.vue'\nimport type { ResponseInstance } from '@/v2/blocks/operation-block/helpers/send-request'\nimport HeadersComponent from '@/v2/blocks/response-block/components/Headers.vue'\nimport ResponseBody from '@/v2/blocks/response-block/components/ResponseBody.vue'\nimport ResponseBodyStreaming from '@/v2/blocks/response-block/components/ResponseBodyStreaming.vue'\nimport ResponseBodyVirtual from '@/v2/blocks/response-block/components/ResponseBodyVirtual.vue'\nimport ResponseCookies from '@/v2/blocks/response-block/components/ResponseCookies.vue'\nimport ResponseEmpty from '@/v2/blocks/response-block/components/ResponseEmpty.vue'\nimport ResponseLoadingOverlay from '@/v2/blocks/response-block/components/ResponseLoadingOverlay.vue'\nimport ResponseMetaInformation from '@/v2/blocks/response-block/components/ResponseMetaInformation.vue'\nimport { textMediaTypes } from '@/v2/blocks/response-block/helpers/media-types'\nimport { parseSetCookie } from '@/v2/blocks/response-block/helpers/parse-set-cookie'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nconst { layout, totalPerformedRequests, response, requestPayload } =\n defineProps<{\n /** Wait for the selected external request example before sending. */\n executionDisabled?: boolean\n /** Preprocessed response */\n response: ResponseInstance | null\n /** Original request as a [url, RequestInit] tuple */\n requestPayload: RequestPayload | null\n /** Client layout */\n layout: ClientLayout\n /** Total number of performed requests */\n totalPerformedRequests: number\n /** Application version */\n appVersion: string\n /** Registered app plugins */\n plugins: ClientPlugin[]\n /** Workspace event bus */\n eventBus: WorkspaceEventBus\n }>()\n\nconst { translate } = useLocalization()\n\n// Headers\nconst responseHeaders = computed(() => {\n const headers = response?.headers\n\n return headers\n ? Object.keys(headers).map((key) => ({\n name: key,\n value: headers[key] ?? '',\n }))\n : []\n})\n\nconst responseContentType = computed(\n () => response?.headers['content-type'] ?? response?.headers['Content-Type'],\n)\n\n// Cookies\nconst responseCookies = computed(\n () =>\n response?.cookieHeaderKeys\n .map((setCookieValue) => parseSetCookie(setCookieValue))\n .filter(isDefined) ?? [],\n)\n\nconst responseSections = ['Cookies', 'Headers', 'Body'] as const\ntype Filter = 'All' | (typeof responseSections)[number]\nconst activeFilter = ref<Filter>('All')\n\nconst filters = computed<Filter[]>(() => ['All', ...responseSections])\n\nconst filterIds = computed<Record<Filter, string>>(() => ({\n All: useId(),\n Cookies: useId(),\n Headers: useId(),\n Body: useId(),\n}))\n\n/** Threshold for virtualizing response bodies in bytes */\nconst VIRTUALIZATION_THRESHOLD = 200_000\nconst shouldVirtualize = computed(() => {\n if (!response || !('size' in response)) {\n return false\n }\n\n // Get content type from headers\n const contentType =\n response.headers?.['content-type'] || response.headers?.['Content-Type']\n\n // If no content type or response size is small, don't virtualize\n if (!contentType || (response.size ?? 0) <= VIRTUALIZATION_THRESHOLD) {\n return false\n }\n\n // Do not virtualize html\n if (contentType.includes('text/html')) {\n return false\n }\n\n // Check if content type matches any text-based type\n const isTextBased = textMediaTypes.some((type) => contentType.includes(type))\n\n return isTextBased && (response.size ?? 0) > VIRTUALIZATION_THRESHOLD\n})\n\nconst requestHeaders = computed(() => {\n const headers = requestPayload?.[1]?.headers\n if (!headers) {\n return []\n }\n\n // Normalise via the Headers constructor, and then spread\n return [...new Headers(headers)].map(([name, value]) => ({\n name,\n value,\n required: false,\n }))\n})\n\nconst isSectionVisible = (\n section: (typeof responseSections)[number] | 'All',\n) => {\n if (activeFilter.value === 'All' || activeFilter.value === section) {\n return true\n }\n return false\n}\n\ndefineExpose({\n responseHeaders,\n responseCookies,\n requestHeaders,\n shouldVirtualize,\n activeFilter,\n filters,\n})\n\nconst filterLabels = computed(() => ({\n All: translate('apiClient.sectionFilter.all'),\n Auth: translate('apiClient.sectionFilter.auth'),\n Variables: translate('apiClient.sectionFilter.variables'),\n Cookies: translate('apiClient.sectionFilter.cookies'),\n Headers: translate('apiClient.sectionFilter.headers'),\n Query: translate('apiClient.sectionFilter.query'),\n Body: translate('apiClient.sectionFilter.body'),\n}))\n</script>\n<template>\n <!-- Bound the stacked response section so long bodies keep their own scroll area. -->\n <ViewLayoutSection\n :aria-label=\"translate('apiClient.responseBlock.response')\"\n class=\"max-xl:max-h-full\">\n <template #title>\n <div class=\"flex h-8 flex-1 items-center\">\n <div\n aria-live=\"polite\"\n class=\"flex items-center\"\n :class=\"{ 'animate-response-heading': response }\">\n <span class=\"response-heading pointer-events-none absolute\">{{\n translate('apiClient.responseBlock.response')\n }}</span>\n <ResponseMetaInformation\n v-if=\"response\"\n class=\"animate-response-children\"\n :eventBus=\"eventBus\"\n :response=\"response\" />\n </div>\n <SectionFilter\n v-model=\"activeFilter\"\n :filters=\"filters\"\n :labels=\"filterLabels\" />\n </div>\n </template>\n <div\n :id=\"filterIds.All\"\n class=\"custom-scroll response-section-content relative grid h-full justify-stretch\"\n :class=\"{\n 'content-start': response,\n }\"\n :role=\"activeFilter === 'All' && response ? 'tabpanel' : 'none'\">\n <template v-if=\"!response\">\n <ResponseEmpty\n :appVersion=\"appVersion\"\n :executionDisabled\n :layout=\"layout\"\n :totalPerformedRequests=\"totalPerformedRequests\"\n @addRequest=\"\n eventBus.emit('ui:open:command-palette', {\n action: 'create-request',\n payload: undefined,\n })\n \"\n @openCommandPalette=\"eventBus.emit('ui:open:command-palette')\"\n @sendRequest=\"eventBus.emit('operation:send:request:hotkey')\" />\n </template>\n <template v-else>\n <!-- Cookies section -->\n <ResponseCookies\n v-if=\"isSectionVisible('Cookies')\"\n :id=\"filterIds.Cookies\"\n class=\"response-section-content-cookies\"\n :cookies=\"responseCookies\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n <!-- Request headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"requestHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.requestHeaders') }}\n </template>\n </HeadersComponent>\n <!-- Response headers section -->\n <HeadersComponent\n v-if=\"isSectionVisible('Headers')\"\n :id=\"filterIds.Headers\"\n class=\"response-section-content-headers\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\">\n <template #title>\n {{ translate('apiClient.responseBlock.responseHeaders') }}\n </template>\n </HeadersComponent>\n\n <!-- Inject response section plugin components -->\n <ScalarErrorBoundary\n v-for=\"(plugin, index) in plugins\"\n :key=\"index\">\n <component\n :is=\"plugin.components.response.component\"\n v-if=\"plugin?.components?.response\"\n v-show=\"activeFilter === 'All'\"\n v-bind=\"plugin.components.response.additionalProps\" />\n </ScalarErrorBoundary>\n\n <template v-if=\"activeFilter === 'All' || activeFilter === 'Body'\">\n <!-- Streaming response body -->\n <ResponseBodyStreaming\n v-if=\"'reader' in response\"\n :id=\"filterIds.Body\"\n class=\"response-section-content-body\"\n :contentType=\"responseContentType\"\n :reader=\"response.reader\" />\n\n <!-- Virtualized Text for massive responses -->\n <ResponseBodyVirtual\n v-else-if=\"shouldVirtualize && typeof response?.data === 'string'\"\n :id=\"filterIds.Body\"\n :content=\"response!.data\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\" />\n\n <!-- Regular response body -->\n <ResponseBody\n v-else\n :id=\"filterIds.Body\"\n :active=\"true\"\n class=\"response-section-content-body\"\n :data=\"response?.data\"\n :headers=\"responseHeaders\"\n layout=\"client\"\n :plugins=\"plugins\"\n :role=\"activeFilter === 'All' ? 'none' : 'tabpanel'\"\n :title=\"translate('apiClient.responseBlock.body')\" />\n </template>\n </template>\n <ResponseLoadingOverlay :eventBus=\"eventBus\" />\n </div>\n </ViewLayoutSection>\n</template>\n<style scoped>\n.animate-response-heading .response-heading {\n animation: push-response 0.2s ease-in-out forwards;\n opacity: 1;\n}\n@keyframes push-response {\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(-4px);\n }\n}\n.animate-response-heading .animate-response-children {\n animation: response-spans 0.2s ease-in-out forwards 0.05s;\n opacity: 0;\n}\n\n@keyframes response-spans {\n from {\n opacity: 0;\n transform: translateY(4px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddressBar.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AddressBar.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"names":[],"mappings":"AA+tBA,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EAClB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAA;AAqBhG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,OAA0B,EAAE,KAAK,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAGzE;;;;GAIG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,MAAM,MAAM,eAAe,GAAG;IAC5B,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,gEAAgE;IAChE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,4BAA4B;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAA;IAClB,gCAAgC;IAChC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;IAC3B,mDAAmD;IACnD,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,8BAA8B;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAA;IAClB,oBAAoB;IACpB,MAAM,EAAE,YAAY,CAAA;IACpB,gBAAgB;IAChB,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,kBAAkB;IAClB,WAAW,EAAE,kBAAkB,CAAA;IAC/B,sCAAsC;IACtC,UAAU,EAAE,UAAU,CAAA;CACvB,CAAA;AACD,QAAA,MAAM,YAAY;;;;;eAqB6B,MAAM;;;;;;eAAN,MAAM;;;;kFAy8BjD,CAAC"}
|
|
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_
|
|
|
2
2
|
import AddressBar_vue_vue_type_script_setup_true_lang_default from "./AddressBar.vue.script.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue
|
|
5
|
-
var AddressBar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(AddressBar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
5
|
+
var AddressBar_default = /*#__PURE__*/ _plugin_vue_export_helper_default(AddressBar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-6edfa5a3"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { AddressBar_default as default };
|
|
8
8
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddressBar.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** An external example must finish loading before sending. */\n executionDisabled?: boolean\n /** Current request path */\n path: string\n /** Current request method */\n method: string\n /** Whether the request target belongs to an OpenAPI webhook. */\n isWebhook?: boolean\n /** Openapi document slug */\n documentSlug: string\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { getHttpMethodInfo } from '@scalar/helpers/http/http-info'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { getOperationExampleKey } from '@/v2/blocks/operation-block/helpers/response-cache'\nimport { isPlaceholderPath } from '@/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path'\nimport { refocusBlurTarget } from '@/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { usePathMasking } from '@/v2/blocks/scalar-address-bar-block/hooks/use-path-masking'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n exampleKey,\n documentSlug,\n server,\n servers,\n environment,\n serverMeta,\n isWebhook = false,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the full destination URL used to deliver a webhook. */\n (e: 'update:webhook-url', url: string): void\n}>()\n\nconst { translate } = useLocalization()\n\n// ───────────────────────────────────────────────────────────────────\n// Template refs & reactive state\n// ───────────────────────────────────────────────────────────────────\n\nconst id = useId()\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst mobileSendButtonRef = useTemplateRef('mobileSendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\n\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<string | null>(null)\nconst tabbedOut = ref(false)\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n// ───────────────────────────────────────────────────────────────────\n// Derived state\n// ───────────────────────────────────────────────────────────────────\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\n/** Animated background transform for the loading indicator */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${getHttpMethodInfo(method).colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/**\n * Placeholder for the path field.\n *\n * Webhooks have no server to prefix the path, so the field doubles as the full\n * destination URL. Hint that a complete URL (including the host) is expected,\n * rather than a bare path.\n */\nconst pathPlaceholder = computed(() => {\n if (server) {\n return ''\n }\n return isWebhook\n ? translate('apiClient.addressBar.webhookUrlPlaceholder')\n : translate('apiClient.addressBar.urlPlaceholder')\n})\n\n/** Whether either dropdown (server or history) is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\n/** Uniquely identifies the currently selected operation + example */\nconst uniqueKey = computed(() =>\n getOperationExampleKey(method, path, exampleKey, documentSlug),\n)\n\n/** Clear conflict state when switching to a different operation */\nwatch(uniqueKey, () => {\n pathConflict.value = null\n methodConflict.value = null\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Focus helpers\n// ───────────────────────────────────────────────────────────────────\n\nconst handleFocusSendButton = (): void => {\n const desktop = sendButtonRef.value?.$el\n const mobile = mobileSendButtonRef.value?.$el\n\n // Focus whichever send button is currently visible\n if (desktop && desktop.offsetParent !== null) {\n desktop.focus()\n } else {\n mobile?.focus()\n }\n}\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n): void => {\n // On non-desktop layouts, if already focused we let the browser handle the\n // native behavior (e.g. selecting the browser's own address bar).\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path masking\n//\n// Drafts on `/` and auto-generated `/_scalar_temp...` paths are internal\n// placeholders — the address bar focuses and clears on mount and on\n// navigation so the user sees a blank prompt instead of the internal\n// path. The deferred mask only clears when CodeMirror still contains that\n// placeholder path, so a user edit that lands before the frame is preserved.\n// ───────────────────────────────────────────────────────────────────\n\nusePathMasking({\n isReady: () => addressBarRef.value?.codeMirror,\n operationKey: () => uniqueKey.value,\n shouldMask: () => isPlaceholderPath(path, documentSlug),\n // Defer to the next frame so focus() runs after click-handler side\n // effects that move focus (e.g. a dropdown refocusing its trigger),\n // which would otherwise blur our input and emit a spurious path\n // update against the now-empty value.\n onMask: () =>\n requestAnimationFrame(() => {\n const editorContent =\n addressBarRef.value?.codeMirror?.state.doc.toString()\n\n if (editorContent && editorContent !== path) {\n return\n }\n\n handleFocusAddressBar({ clear: true })\n }),\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Server extraction\n// ───────────────────────────────────────────────────────────────────\n\n/** If the path contains a server URL, extract it and select or add that server */\nconst extractAndSelectServer = (targetPath: string): string => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, remainingPath] = extracted\n\n // Server is already selected — nothing to do\n if (url === server?.url) {\n return remainingPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n if (matchingServer) {\n eventBus.emit('server:update:selected', { url, meta: serverMeta })\n } else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: { type: 'operation', path, method },\n })\n }\n\n return remainingPath\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path / method updates\n// ───────────────────────────────────────────────────────────────────\n\nconst normalizePath = (value: string): string =>\n value.startsWith('/') ? value : `/${value}`\n\n/** Emit a path/method update and reconcile conflicts + cursor state on the result */\nconst emitPathMethodUpdate = (\n targetMethod: string,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n if (isWebhook) {\n // A webhook has no OpenAPI server, so the field holds the full destination\n // URL. Keep exactly what the user entered — do not split it into an invisible\n // server plus a leftover path, and do not force a leading slash. That split\n // is what made a typed URL collapse to a stray \"/\" on blur.\n const webhookUrl = targetPath.trim()\n addressBarRef.value?.setCodeMirrorContent(webhookUrl)\n emit('update:webhook-url', webhookUrl)\n methodConflict.value = null\n pathConflict.value = null\n nextTick(() => refocusBlurTarget(blurTargetSelector))\n return\n }\n\n const extractedPath = extractAndSelectServer(targetPath)\n const normalizedPath = normalizePath(extractedPath)\n\n // Keep CodeMirror in sync so a conflict does not leave a stale value on screen\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, returnedSelector) => {\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n } else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n\n return\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path\n // unchanged. CodeMirror still shows the full URL, so force it back to\n // just the path.\n const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent\n if (\n status === 'no-change' &&\n mirrorContent &&\n mirrorContent !== extractedPath\n ) {\n addressBarRef.value?.setCodeMirrorContent(extractedPath)\n }\n\n nextTick(() => refocusBlurTarget(returnedSelector))\n },\n })\n}\n\n/** Change the operation's method, preferring the conflicting path if present */\nconst handleMethodChange = (newMethod: string): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Save the path on blur and replay the click that caused the blur (so e.g. a\n * click on the Send button still fires after the async update resolves).\n *\n * Tab-outs do not replay — tabbing to a button should not trigger its click\n * (`tabbedOut` is set on `@keydown.tab`).\n *\n * Programmatic focus moves also do not replay. On first navigation into a\n * draft operation, `usePathMasking` focuses and clears the address bar while\n * `ScalarSidebarNestedItems` focuses the drilled-in Back button in\n * `nextTick`. That steals focus from CodeMirror, fires blur with a\n * `relatedTarget` (the Back button), and without a guard we would treat it\n * like a user click: capture the selector, then `refocusBlurTarget` would\n * synthesize `.click()` on Back and navigate away.\n *\n * `FocusEvent.sourceCapabilities` is null when no input device caused the\n * focus change (e.g. `element.focus()`). A real pointer click sets it on the\n * blur that precedes the click, so we still replay Send and other buttons.\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget =\n event.relatedTarget instanceof Element ? event.relatedTarget : null\n const blurTargetSelector =\n tabbedOut.value ||\n ('sourceCapabilities' in event && event.sourceCapabilities === null)\n ? null\n : getSelector(relatedTarget)\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Save the path on Enter and trigger the Send button after the update resolves */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener from also handling this Enter press\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Unset the server when backspace is pressed on an empty path */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if (event.target instanceof HTMLElement && event.target.innerText === '\\n') {\n // A webhook has no server to unset; its full URL lives in the field itself.\n if (isWebhook) {\n return\n }\n eventBus.emit('server:update:selected', { url: '', meta: serverMeta })\n }\n}\n\nconst handleServerSelect = (\n payload: ApiReferenceEvents['server:update:selected'],\n): void => {\n eventBus.emit('server:update:selected', payload)\n}\n\nconst handleServerVariable = (\n payload: ApiReferenceEvents['server:update:variables'],\n): void => {\n eventBus.emit('server:update:variables', payload)\n}\n\n/** Address bar copy is handled in OperationBlock (same URL as Send). */\nconst requestCopyUrl = (): void => {\n eventBus.emit('copy-url:address-bar')\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Navigation\n// ───────────────────────────────────────────────────────────────────\n\nconst navigateToServersPage = (): void => {\n if (serverMeta.type === 'operation') {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n return\n }\n\n eventBus.emit('ui:navigate', { page: 'document', path: 'servers' })\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Lifecycle\n// ───────────────────────────────────────────────────────────────────\n\nconst unsubscribes: Array<() => void> = []\n\nonMounted(() => {\n unsubscribes.push(\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar),\n eventBus.on('ui:focus:send-button', handleFocusSendButton),\n eventBus.on('hooks:on:request:sent', startLoading),\n eventBus.on('hooks:on:request:complete', stopLoading),\n )\n})\n\nonBeforeUnmount(() => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n\n // Prevents the loading animation from continuing after unmount\n stopLoading()\n})\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <!--\n Address bar.\n\n The wide-container layout matches the original single-row bar:\n `[Method | URL | Copy | History | Send]`. When the surrounding\n `@container` drops below `@3xl` the bar collapses to\n `[URL | History]` and a second row appears beneath it with a\n duplicate `Method`, `Copy`, and `Send` so the URL gets the full\n container width while every action stays on the same line as the\n send button.\n -->\n <div\n class=\"order-last flex h-auto w-full max-w-3xl grow-3 flex-wrap items-stretch [--scalar-address-bar-height:32px] @3xl:order-0 @3xl:flex-nowrap\">\n <!-- Address Bar -->\n <div\n :id=\"id\"\n class=\"address-bar-bg-states text-xxs group relative flex h-(--scalar-address-bar-height) w-full flex-1 flex-row items-stretch rounded-lg p-0.75\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n\n <!--\n Method, Copy, and Send are hidden in mobile mode (container\n narrower than `@3xl`) and the duplicate buttons in the trailing\n mobile actions row take over at that point.\n -->\n <div class=\"hidden @3xl:flex\">\n <HttpMethod\n :isEditable=\"layout !== 'modal' && !isWebhook\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\"\n dir=\"ltr\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"isWebhook ? 'modal' : layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"handleServerSelect\"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"handleServerVariable\" />\n\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n :aria-label=\"translate('apiClient.addressBar.path')\"\n class=\"ml-1 min-w-fit pl-px outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal' && !isWebhook\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"pathPlaceholder\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1 hidden @3xl:flex\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"requestCopyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBar.copyUrl')\n }}</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n {{\n translate('apiClient.addressBar.duplicateRequest', {\n method: (methodConflict ?? method).toUpperCase(),\n path: pathConflict ?? path,\n })\n }}\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative hidden h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold @3xl:flex\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs flex\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n\n <!--\n Mobile actions row. Visible by default and hidden once the\n container reaches `@3xl`, where the duplicate Method / Copy /\n Send buttons move back into the bar itself.\n -->\n <div\n class=\"mt-2 flex h-(--scalar-address-bar-height) w-full items-stretch gap-1 @3xl:hidden\">\n <HttpMethod\n :isEditable=\"layout !== 'modal' && !isWebhook\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n <ScalarButton\n class=\"hover:bg-b-3 ml-auto\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"requestCopyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBar.copyUrl')\n }}</span>\n </ScalarButton>\n <ScalarButton\n ref=\"mobileSendButtonRef\"\n class=\"relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n -ms-overflow-style: none;\n /* Fade the URL at the viewport edges. Mask lives on the scrollport (this\n element) rather than on `.cm-scroller`, whose width follows the URL's\n full content and pushes the right edge off-screen when overflowing. */\n mask-image: linear-gradient(\n to right,\n transparent 0,\n black 6px,\n black calc(100% - 24px),\n transparent 100%\n );\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"AddressBar.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** An external example must finish loading before sending. */\n executionDisabled?: boolean\n /** Current request path */\n path: string\n /** Current request method */\n method: string\n /** Whether the request target belongs to an OpenAPI webhook. */\n isWebhook?: boolean\n /** Openapi document slug */\n documentSlug: string\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { getHttpMethodInfo } from '@scalar/helpers/http/http-info'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { getOperationExampleKey } from '@/v2/blocks/operation-block/helpers/response-cache'\nimport { isPlaceholderPath } from '@/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path'\nimport { refocusBlurTarget } from '@/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { usePathMasking } from '@/v2/blocks/scalar-address-bar-block/hooks/use-path-masking'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n exampleKey,\n documentSlug,\n server,\n servers,\n environment,\n serverMeta,\n isWebhook = false,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the full destination URL used to deliver a webhook. */\n (e: 'update:webhook-url', url: string): void\n}>()\n\nconst { translate } = useLocalization()\n\n// ───────────────────────────────────────────────────────────────────\n// Template refs & reactive state\n// ───────────────────────────────────────────────────────────────────\n\nconst id = useId()\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst mobileSendButtonRef = useTemplateRef('mobileSendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\n\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<string | null>(null)\nconst tabbedOut = ref(false)\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n// ───────────────────────────────────────────────────────────────────\n// Derived state\n// ───────────────────────────────────────────────────────────────────\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\n/** Animated background transform for the loading indicator */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${getHttpMethodInfo(method).colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/**\n * Placeholder for the path field.\n *\n * Webhooks have no server to prefix the path, so the field doubles as the full\n * destination URL. Hint that a complete URL (including the host) is expected,\n * rather than a bare path.\n */\nconst pathPlaceholder = computed(() => {\n if (server) {\n return ''\n }\n return isWebhook\n ? translate('apiClient.addressBar.webhookUrlPlaceholder')\n : translate('apiClient.addressBar.urlPlaceholder')\n})\n\n/** Whether either dropdown (server or history) is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\n/** Uniquely identifies the currently selected operation + example */\nconst uniqueKey = computed(() =>\n getOperationExampleKey(method, path, exampleKey, documentSlug),\n)\n\n/** Clear conflict state when switching to a different operation */\nwatch(uniqueKey, () => {\n pathConflict.value = null\n methodConflict.value = null\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Focus helpers\n// ───────────────────────────────────────────────────────────────────\n\nconst handleFocusSendButton = (): void => {\n const desktop = sendButtonRef.value?.$el\n const mobile = mobileSendButtonRef.value?.$el\n\n // Focus whichever send button is currently visible\n if (desktop && desktop.offsetParent !== null) {\n desktop.focus()\n } else {\n mobile?.focus()\n }\n}\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n): void => {\n // On non-desktop layouts, if already focused we let the browser handle the\n // native behavior (e.g. selecting the browser's own address bar).\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path masking\n//\n// Drafts on `/` and auto-generated `/_scalar_temp...` paths are internal\n// placeholders — the address bar focuses and clears on mount and on\n// navigation so the user sees a blank prompt instead of the internal\n// path. The deferred mask only clears when CodeMirror still contains that\n// placeholder path, so a user edit that lands before the frame is preserved.\n// ───────────────────────────────────────────────────────────────────\n\nusePathMasking({\n isReady: () => addressBarRef.value?.codeMirror,\n operationKey: () => uniqueKey.value,\n shouldMask: () => isPlaceholderPath(path, documentSlug),\n // Defer to the next frame so focus() runs after click-handler side\n // effects that move focus (e.g. a dropdown refocusing its trigger),\n // which would otherwise blur our input and emit a spurious path\n // update against the now-empty value.\n onMask: () =>\n requestAnimationFrame(() => {\n const editorContent =\n addressBarRef.value?.codeMirror?.state.doc.toString()\n\n if (editorContent && editorContent !== path) {\n return\n }\n\n handleFocusAddressBar({ clear: true })\n }),\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Server extraction\n// ───────────────────────────────────────────────────────────────────\n\n/** If the path contains a server URL, extract it and select or add that server */\nconst extractAndSelectServer = (targetPath: string): string => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, remainingPath] = extracted\n\n // Server is already selected — nothing to do\n if (url === server?.url) {\n return remainingPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n if (matchingServer) {\n eventBus.emit('server:update:selected', { url, meta: serverMeta })\n } else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: { type: 'operation', path, method },\n })\n }\n\n return remainingPath\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path / method updates\n// ───────────────────────────────────────────────────────────────────\n\nconst normalizePath = (value: string): string =>\n value.startsWith('/') ? value : `/${value}`\n\n/** Emit a path/method update and reconcile conflicts + cursor state on the result */\nconst emitPathMethodUpdate = (\n targetMethod: string,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n if (isWebhook) {\n // A webhook has no OpenAPI server, so the field holds the full destination\n // URL. Keep exactly what the user entered — do not split it into an invisible\n // server plus a leftover path, and do not force a leading slash. That split\n // is what made a typed URL collapse to a stray \"/\" on blur.\n const webhookUrl = targetPath.trim()\n addressBarRef.value?.setCodeMirrorContent(webhookUrl)\n emit('update:webhook-url', webhookUrl)\n methodConflict.value = null\n pathConflict.value = null\n nextTick(() => refocusBlurTarget(blurTargetSelector))\n return\n }\n\n const extractedPath = extractAndSelectServer(targetPath)\n const normalizedPath = normalizePath(extractedPath)\n\n // Keep CodeMirror in sync so a conflict does not leave a stale value on screen\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, returnedSelector) => {\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n } else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n\n return\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path\n // unchanged. CodeMirror still shows the full URL, so force it back to\n // just the path.\n const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent\n if (\n status === 'no-change' &&\n mirrorContent &&\n mirrorContent !== extractedPath\n ) {\n addressBarRef.value?.setCodeMirrorContent(extractedPath)\n }\n\n nextTick(() => refocusBlurTarget(returnedSelector))\n },\n })\n}\n\n/** Change the operation's method, preferring the conflicting path if present */\nconst handleMethodChange = (newMethod: string): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Save the path on blur and replay the click that caused the blur (so e.g. a\n * click on the Send button still fires after the async update resolves).\n *\n * Tab-outs do not replay — tabbing to a button should not trigger its click\n * (`tabbedOut` is set on `@keydown.tab`).\n *\n * Programmatic focus moves also do not replay. On first navigation into a\n * draft operation, `usePathMasking` focuses and clears the address bar while\n * `ScalarSidebarNestedItems` focuses the drilled-in Back button in\n * `nextTick`. That steals focus from CodeMirror, fires blur with a\n * `relatedTarget` (the Back button), and without a guard we would treat it\n * like a user click: capture the selector, then `refocusBlurTarget` would\n * synthesize `.click()` on Back and navigate away.\n *\n * `FocusEvent.sourceCapabilities` is null when no input device caused the\n * focus change (e.g. `element.focus()`). A real pointer click sets it on the\n * blur that precedes the click, so we still replay Send and other buttons.\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget =\n event.relatedTarget instanceof Element ? event.relatedTarget : null\n const blurTargetSelector =\n tabbedOut.value ||\n ('sourceCapabilities' in event && event.sourceCapabilities === null)\n ? null\n : getSelector(relatedTarget)\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Save the path on Enter and trigger the Send button after the update resolves */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener from also handling this Enter press\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Unset the server when backspace is pressed on an empty path */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if (event.target instanceof HTMLElement && event.target.innerText === '\\n') {\n // A webhook has no server to unset; its full URL lives in the field itself.\n if (isWebhook) {\n return\n }\n eventBus.emit('server:update:selected', { url: '', meta: serverMeta })\n }\n}\n\nconst handleServerSelect = (\n payload: ApiReferenceEvents['server:update:selected'],\n): void => {\n eventBus.emit('server:update:selected', payload)\n}\n\nconst handleServerVariable = (\n payload: ApiReferenceEvents['server:update:variables'],\n): void => {\n eventBus.emit('server:update:variables', payload)\n}\n\n/** Address bar copy is handled in OperationBlock (same URL as Send). */\nconst requestCopyUrl = (): void => {\n eventBus.emit('copy-url:address-bar')\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Navigation\n// ───────────────────────────────────────────────────────────────────\n\nconst navigateToServersPage = (): void => {\n if (serverMeta.type === 'operation') {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n return\n }\n\n eventBus.emit('ui:navigate', { page: 'document', path: 'servers' })\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Lifecycle\n// ───────────────────────────────────────────────────────────────────\n\nconst unsubscribes: Array<() => void> = []\n\nonMounted(() => {\n unsubscribes.push(\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar),\n eventBus.on('ui:focus:send-button', handleFocusSendButton),\n eventBus.on('hooks:on:request:sent', startLoading),\n eventBus.on('hooks:on:request:complete', stopLoading),\n )\n})\n\nonBeforeUnmount(() => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n\n // Prevents the loading animation from continuing after unmount\n stopLoading()\n})\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <!--\n Address bar.\n\n The wide-container layout is a single row:\n `[Method | URL | Copy | History | Send]`. When the surrounding\n `@container` drops below `@3xl` only the send button moves to a\n second row, where it spans the full width. The method and copy\n controls stay inside the bar at every width so they keep belonging\n to the URL they act on rather than floating beside it.\n -->\n <div\n class=\"order-last flex h-auto w-full max-w-3xl grow-3 flex-wrap items-stretch [--scalar-address-bar-height:32px] @3xl:order-0 @3xl:flex-nowrap\">\n <!-- Address Bar -->\n <div\n :id=\"id\"\n class=\"address-bar-bg-states text-xxs group relative flex h-(--scalar-address-bar-height) w-full flex-1 flex-row items-stretch rounded-lg p-0.75\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n\n <!-- Only Send leaves the bar below `@3xl`; see the trailing row. -->\n <div class=\"flex\">\n <HttpMethod\n :isEditable=\"layout !== 'modal' && !isWebhook\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\"\n dir=\"ltr\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"isWebhook ? 'modal' : layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"handleServerSelect\"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"handleServerVariable\" />\n\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n :aria-label=\"translate('apiClient.addressBar.path')\"\n class=\"ml-1 min-w-fit pl-px outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal' && !isWebhook\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"pathPlaceholder\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1 flex\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"requestCopyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBar.copyUrl')\n }}</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n {{\n translate('apiClient.addressBar.duplicateRequest', {\n method: (methodConflict ?? method).toUpperCase(),\n path: pathConflict ?? path,\n })\n }}\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative hidden h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold @3xl:flex\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs flex\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n\n <!--\n Below `@3xl` the send button moves out of the bar onto its own row\n and spans the full width, so the primary action stays easy to hit\n once the container is too narrow to hold it beside the URL.\n -->\n <ScalarButton\n ref=\"mobileSendButtonRef\"\n class=\"relative mt-2 h-(--scalar-address-bar-height) w-full shrink-0 justify-center overflow-hidden py-1 font-bold @3xl:hidden\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n -ms-overflow-style: none;\n /* Fade the URL at the viewport edges. Mask lives on the scrollport (this\n element) rather than on `.cm-scroller`, whose width follows the URL's\n full content and pushes the right edge off-screen when overflowing. */\n mask-image: linear-gradient(\n to right,\n transparent 0,\n black 6px,\n black calc(100% - 24px),\n transparent 100%\n );\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":""}
|
|
@@ -19,7 +19,7 @@ import { extractServerFromPath } from "@scalar/helpers/url/extract-server-from-p
|
|
|
19
19
|
//#region src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue?vue&type=script&setup=true&lang.ts
|
|
20
20
|
var _hoisted_1 = { class: "order-last flex h-auto w-full max-w-3xl grow-3 flex-wrap items-stretch [--scalar-address-bar-height:32px] @3xl:order-0 @3xl:flex-nowrap" };
|
|
21
21
|
var _hoisted_2 = ["id"];
|
|
22
|
-
var _hoisted_3 = { class: "
|
|
22
|
+
var _hoisted_3 = { class: "flex" };
|
|
23
23
|
var _hoisted_4 = {
|
|
24
24
|
class: "scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal",
|
|
25
25
|
dir: "ltr"
|
|
@@ -37,14 +37,12 @@ var _hoisted_9 = {
|
|
|
37
37
|
};
|
|
38
38
|
var _hoisted_10 = { class: "text-xxs flex" };
|
|
39
39
|
var _hoisted_11 = { class: "sr-only" };
|
|
40
|
-
var _hoisted_12 = {
|
|
41
|
-
var _hoisted_13 = { class: "sr-only" };
|
|
42
|
-
var _hoisted_14 = {
|
|
40
|
+
var _hoisted_12 = {
|
|
43
41
|
"aria-hidden": "true",
|
|
44
42
|
class: "inline-flex items-center gap-1"
|
|
45
43
|
};
|
|
46
|
-
var
|
|
47
|
-
var
|
|
44
|
+
var _hoisted_13 = { class: "text-xxs" };
|
|
45
|
+
var _hoisted_14 = { class: "sr-only" };
|
|
48
46
|
var AddressBar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
49
47
|
name: "AddressBar",
|
|
50
48
|
props: {
|
|
@@ -341,7 +339,7 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
341
339
|
"placeholder"
|
|
342
340
|
])]),
|
|
343
341
|
createVNode(unref(ScalarButton), {
|
|
344
|
-
class: "hover:bg-b-3 mx-1
|
|
342
|
+
class: "hover:bg-b-3 mx-1 flex",
|
|
345
343
|
size: "xs",
|
|
346
344
|
variant: "ghost",
|
|
347
345
|
onClick: requestCopyUrl
|
|
@@ -377,42 +375,24 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defin
|
|
|
377
375
|
})), 1)]),
|
|
378
376
|
_: 1
|
|
379
377
|
}, 8, ["disabled"])
|
|
380
|
-
], 10, _hoisted_2),
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
createVNode(unref(
|
|
389
|
-
class: "
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
ref_key: "mobileSendButtonRef",
|
|
399
|
-
ref: mobileSendButtonRef,
|
|
400
|
-
class: "relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold",
|
|
401
|
-
"data-addressbar-action": "send",
|
|
402
|
-
disabled: unref(isLoading) || __props.executionDisabled,
|
|
403
|
-
onClick: _cache[5] || (_cache[5] = ($event) => emit("execute"))
|
|
404
|
-
}, {
|
|
405
|
-
default: withCtx(() => [createElementVNode("span", _hoisted_14, [createVNode(unref(ScalarIcon), {
|
|
406
|
-
class: "relative shrink-0 fill-current",
|
|
407
|
-
icon: "Play",
|
|
408
|
-
size: "xs"
|
|
409
|
-
}), createElementVNode("span", _hoisted_15, toDisplayString(unref(translate)("apiClient.addressBar.send")), 1)]), createElementVNode("span", _hoisted_16, toDisplayString(unref(translate)("apiClient.addressBar.sendRequest", {
|
|
410
|
-
method: __props.method,
|
|
411
|
-
url: `${__props.server?.url ?? ""}${__props.path}`
|
|
412
|
-
})), 1)]),
|
|
413
|
-
_: 1
|
|
414
|
-
}, 8, ["disabled"])
|
|
415
|
-
])]);
|
|
378
|
+
], 10, _hoisted_2), createVNode(unref(ScalarButton), {
|
|
379
|
+
ref_key: "mobileSendButtonRef",
|
|
380
|
+
ref: mobileSendButtonRef,
|
|
381
|
+
class: "relative mt-2 h-(--scalar-address-bar-height) w-full shrink-0 justify-center overflow-hidden py-1 font-bold @3xl:hidden",
|
|
382
|
+
"data-addressbar-action": "send",
|
|
383
|
+
disabled: unref(isLoading) || __props.executionDisabled,
|
|
384
|
+
onClick: _cache[5] || (_cache[5] = ($event) => emit("execute"))
|
|
385
|
+
}, {
|
|
386
|
+
default: withCtx(() => [createElementVNode("span", _hoisted_12, [createVNode(unref(ScalarIcon), {
|
|
387
|
+
class: "relative shrink-0 fill-current",
|
|
388
|
+
icon: "Play",
|
|
389
|
+
size: "xs"
|
|
390
|
+
}), createElementVNode("span", _hoisted_13, toDisplayString(unref(translate)("apiClient.addressBar.send")), 1)]), createElementVNode("span", _hoisted_14, toDisplayString(unref(translate)("apiClient.addressBar.sendRequest", {
|
|
391
|
+
method: __props.method,
|
|
392
|
+
url: `${__props.server?.url ?? ""}${__props.path}`
|
|
393
|
+
})), 1)]),
|
|
394
|
+
_: 1
|
|
395
|
+
}, 8, ["disabled"])]);
|
|
416
396
|
};
|
|
417
397
|
}
|
|
418
398
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AddressBar.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** An external example must finish loading before sending. */\n executionDisabled?: boolean\n /** Current request path */\n path: string\n /** Current request method */\n method: string\n /** Whether the request target belongs to an OpenAPI webhook. */\n isWebhook?: boolean\n /** Openapi document slug */\n documentSlug: string\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { getHttpMethodInfo } from '@scalar/helpers/http/http-info'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { getOperationExampleKey } from '@/v2/blocks/operation-block/helpers/response-cache'\nimport { isPlaceholderPath } from '@/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path'\nimport { refocusBlurTarget } from '@/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { usePathMasking } from '@/v2/blocks/scalar-address-bar-block/hooks/use-path-masking'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n exampleKey,\n documentSlug,\n server,\n servers,\n environment,\n serverMeta,\n isWebhook = false,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the full destination URL used to deliver a webhook. */\n (e: 'update:webhook-url', url: string): void\n}>()\n\nconst { translate } = useLocalization()\n\n// ───────────────────────────────────────────────────────────────────\n// Template refs & reactive state\n// ───────────────────────────────────────────────────────────────────\n\nconst id = useId()\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst mobileSendButtonRef = useTemplateRef('mobileSendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\n\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<string | null>(null)\nconst tabbedOut = ref(false)\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n// ───────────────────────────────────────────────────────────────────\n// Derived state\n// ───────────────────────────────────────────────────────────────────\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\n/** Animated background transform for the loading indicator */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${getHttpMethodInfo(method).colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/**\n * Placeholder for the path field.\n *\n * Webhooks have no server to prefix the path, so the field doubles as the full\n * destination URL. Hint that a complete URL (including the host) is expected,\n * rather than a bare path.\n */\nconst pathPlaceholder = computed(() => {\n if (server) {\n return ''\n }\n return isWebhook\n ? translate('apiClient.addressBar.webhookUrlPlaceholder')\n : translate('apiClient.addressBar.urlPlaceholder')\n})\n\n/** Whether either dropdown (server or history) is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\n/** Uniquely identifies the currently selected operation + example */\nconst uniqueKey = computed(() =>\n getOperationExampleKey(method, path, exampleKey, documentSlug),\n)\n\n/** Clear conflict state when switching to a different operation */\nwatch(uniqueKey, () => {\n pathConflict.value = null\n methodConflict.value = null\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Focus helpers\n// ───────────────────────────────────────────────────────────────────\n\nconst handleFocusSendButton = (): void => {\n const desktop = sendButtonRef.value?.$el\n const mobile = mobileSendButtonRef.value?.$el\n\n // Focus whichever send button is currently visible\n if (desktop && desktop.offsetParent !== null) {\n desktop.focus()\n } else {\n mobile?.focus()\n }\n}\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n): void => {\n // On non-desktop layouts, if already focused we let the browser handle the\n // native behavior (e.g. selecting the browser's own address bar).\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path masking\n//\n// Drafts on `/` and auto-generated `/_scalar_temp...` paths are internal\n// placeholders — the address bar focuses and clears on mount and on\n// navigation so the user sees a blank prompt instead of the internal\n// path. The deferred mask only clears when CodeMirror still contains that\n// placeholder path, so a user edit that lands before the frame is preserved.\n// ───────────────────────────────────────────────────────────────────\n\nusePathMasking({\n isReady: () => addressBarRef.value?.codeMirror,\n operationKey: () => uniqueKey.value,\n shouldMask: () => isPlaceholderPath(path, documentSlug),\n // Defer to the next frame so focus() runs after click-handler side\n // effects that move focus (e.g. a dropdown refocusing its trigger),\n // which would otherwise blur our input and emit a spurious path\n // update against the now-empty value.\n onMask: () =>\n requestAnimationFrame(() => {\n const editorContent =\n addressBarRef.value?.codeMirror?.state.doc.toString()\n\n if (editorContent && editorContent !== path) {\n return\n }\n\n handleFocusAddressBar({ clear: true })\n }),\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Server extraction\n// ───────────────────────────────────────────────────────────────────\n\n/** If the path contains a server URL, extract it and select or add that server */\nconst extractAndSelectServer = (targetPath: string): string => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, remainingPath] = extracted\n\n // Server is already selected — nothing to do\n if (url === server?.url) {\n return remainingPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n if (matchingServer) {\n eventBus.emit('server:update:selected', { url, meta: serverMeta })\n } else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: { type: 'operation', path, method },\n })\n }\n\n return remainingPath\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path / method updates\n// ───────────────────────────────────────────────────────────────────\n\nconst normalizePath = (value: string): string =>\n value.startsWith('/') ? value : `/${value}`\n\n/** Emit a path/method update and reconcile conflicts + cursor state on the result */\nconst emitPathMethodUpdate = (\n targetMethod: string,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n if (isWebhook) {\n // A webhook has no OpenAPI server, so the field holds the full destination\n // URL. Keep exactly what the user entered — do not split it into an invisible\n // server plus a leftover path, and do not force a leading slash. That split\n // is what made a typed URL collapse to a stray \"/\" on blur.\n const webhookUrl = targetPath.trim()\n addressBarRef.value?.setCodeMirrorContent(webhookUrl)\n emit('update:webhook-url', webhookUrl)\n methodConflict.value = null\n pathConflict.value = null\n nextTick(() => refocusBlurTarget(blurTargetSelector))\n return\n }\n\n const extractedPath = extractAndSelectServer(targetPath)\n const normalizedPath = normalizePath(extractedPath)\n\n // Keep CodeMirror in sync so a conflict does not leave a stale value on screen\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, returnedSelector) => {\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n } else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n\n return\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path\n // unchanged. CodeMirror still shows the full URL, so force it back to\n // just the path.\n const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent\n if (\n status === 'no-change' &&\n mirrorContent &&\n mirrorContent !== extractedPath\n ) {\n addressBarRef.value?.setCodeMirrorContent(extractedPath)\n }\n\n nextTick(() => refocusBlurTarget(returnedSelector))\n },\n })\n}\n\n/** Change the operation's method, preferring the conflicting path if present */\nconst handleMethodChange = (newMethod: string): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Save the path on blur and replay the click that caused the blur (so e.g. a\n * click on the Send button still fires after the async update resolves).\n *\n * Tab-outs do not replay — tabbing to a button should not trigger its click\n * (`tabbedOut` is set on `@keydown.tab`).\n *\n * Programmatic focus moves also do not replay. On first navigation into a\n * draft operation, `usePathMasking` focuses and clears the address bar while\n * `ScalarSidebarNestedItems` focuses the drilled-in Back button in\n * `nextTick`. That steals focus from CodeMirror, fires blur with a\n * `relatedTarget` (the Back button), and without a guard we would treat it\n * like a user click: capture the selector, then `refocusBlurTarget` would\n * synthesize `.click()` on Back and navigate away.\n *\n * `FocusEvent.sourceCapabilities` is null when no input device caused the\n * focus change (e.g. `element.focus()`). A real pointer click sets it on the\n * blur that precedes the click, so we still replay Send and other buttons.\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget =\n event.relatedTarget instanceof Element ? event.relatedTarget : null\n const blurTargetSelector =\n tabbedOut.value ||\n ('sourceCapabilities' in event && event.sourceCapabilities === null)\n ? null\n : getSelector(relatedTarget)\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Save the path on Enter and trigger the Send button after the update resolves */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener from also handling this Enter press\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Unset the server when backspace is pressed on an empty path */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if (event.target instanceof HTMLElement && event.target.innerText === '\\n') {\n // A webhook has no server to unset; its full URL lives in the field itself.\n if (isWebhook) {\n return\n }\n eventBus.emit('server:update:selected', { url: '', meta: serverMeta })\n }\n}\n\nconst handleServerSelect = (\n payload: ApiReferenceEvents['server:update:selected'],\n): void => {\n eventBus.emit('server:update:selected', payload)\n}\n\nconst handleServerVariable = (\n payload: ApiReferenceEvents['server:update:variables'],\n): void => {\n eventBus.emit('server:update:variables', payload)\n}\n\n/** Address bar copy is handled in OperationBlock (same URL as Send). */\nconst requestCopyUrl = (): void => {\n eventBus.emit('copy-url:address-bar')\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Navigation\n// ───────────────────────────────────────────────────────────────────\n\nconst navigateToServersPage = (): void => {\n if (serverMeta.type === 'operation') {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n return\n }\n\n eventBus.emit('ui:navigate', { page: 'document', path: 'servers' })\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Lifecycle\n// ───────────────────────────────────────────────────────────────────\n\nconst unsubscribes: Array<() => void> = []\n\nonMounted(() => {\n unsubscribes.push(\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar),\n eventBus.on('ui:focus:send-button', handleFocusSendButton),\n eventBus.on('hooks:on:request:sent', startLoading),\n eventBus.on('hooks:on:request:complete', stopLoading),\n )\n})\n\nonBeforeUnmount(() => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n\n // Prevents the loading animation from continuing after unmount\n stopLoading()\n})\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <!--\n Address bar.\n\n The wide-container layout matches the original single-row bar:\n `[Method | URL | Copy | History | Send]`. When the surrounding\n `@container` drops below `@3xl` the bar collapses to\n `[URL | History]` and a second row appears beneath it with a\n duplicate `Method`, `Copy`, and `Send` so the URL gets the full\n container width while every action stays on the same line as the\n send button.\n -->\n <div\n class=\"order-last flex h-auto w-full max-w-3xl grow-3 flex-wrap items-stretch [--scalar-address-bar-height:32px] @3xl:order-0 @3xl:flex-nowrap\">\n <!-- Address Bar -->\n <div\n :id=\"id\"\n class=\"address-bar-bg-states text-xxs group relative flex h-(--scalar-address-bar-height) w-full flex-1 flex-row items-stretch rounded-lg p-0.75\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n\n <!--\n Method, Copy, and Send are hidden in mobile mode (container\n narrower than `@3xl`) and the duplicate buttons in the trailing\n mobile actions row take over at that point.\n -->\n <div class=\"hidden @3xl:flex\">\n <HttpMethod\n :isEditable=\"layout !== 'modal' && !isWebhook\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\"\n dir=\"ltr\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"isWebhook ? 'modal' : layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"handleServerSelect\"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"handleServerVariable\" />\n\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n :aria-label=\"translate('apiClient.addressBar.path')\"\n class=\"ml-1 min-w-fit pl-px outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal' && !isWebhook\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"pathPlaceholder\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1 hidden @3xl:flex\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"requestCopyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBar.copyUrl')\n }}</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n {{\n translate('apiClient.addressBar.duplicateRequest', {\n method: (methodConflict ?? method).toUpperCase(),\n path: pathConflict ?? path,\n })\n }}\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative hidden h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold @3xl:flex\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs flex\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n\n <!--\n Mobile actions row. Visible by default and hidden once the\n container reaches `@3xl`, where the duplicate Method / Copy /\n Send buttons move back into the bar itself.\n -->\n <div\n class=\"mt-2 flex h-(--scalar-address-bar-height) w-full items-stretch gap-1 @3xl:hidden\">\n <HttpMethod\n :isEditable=\"layout !== 'modal' && !isWebhook\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n <ScalarButton\n class=\"hover:bg-b-3 ml-auto\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"requestCopyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBar.copyUrl')\n }}</span>\n </ScalarButton>\n <ScalarButton\n ref=\"mobileSendButtonRef\"\n class=\"relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n -ms-overflow-style: none;\n /* Fade the URL at the viewport edges. Mask lives on the scrollport (this\n element) rather than on `.cm-scroller`, whose width follows the URL's\n full content and pushes the right edge off-screen when overflowing. */\n mask-image: linear-gradient(\n to right,\n transparent 0,\n black 6px,\n black calc(100% - 24px),\n transparent 100%\n );\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AddressBar.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** An external example must finish loading before sending. */\n executionDisabled?: boolean\n /** Current request path */\n path: string\n /** Current request method */\n method: string\n /** Whether the request target belongs to an OpenAPI webhook. */\n isWebhook?: boolean\n /** Openapi document slug */\n documentSlug: string\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { getHttpMethodInfo } from '@scalar/helpers/http/http-info'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { getOperationExampleKey } from '@/v2/blocks/operation-block/helpers/response-cache'\nimport { isPlaceholderPath } from '@/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path'\nimport { refocusBlurTarget } from '@/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { usePathMasking } from '@/v2/blocks/scalar-address-bar-block/hooks/use-path-masking'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport { useLocalization } from '@/v2/features/localization'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n exampleKey,\n documentSlug,\n server,\n servers,\n environment,\n serverMeta,\n isWebhook = false,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n /** Update the full destination URL used to deliver a webhook. */\n (e: 'update:webhook-url', url: string): void\n}>()\n\nconst { translate } = useLocalization()\n\n// ───────────────────────────────────────────────────────────────────\n// Template refs & reactive state\n// ───────────────────────────────────────────────────────────────────\n\nconst id = useId()\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst mobileSendButtonRef = useTemplateRef('mobileSendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\n\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<string | null>(null)\nconst tabbedOut = ref(false)\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n// ───────────────────────────────────────────────────────────────────\n// Derived state\n// ───────────────────────────────────────────────────────────────────\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\n/** Animated background transform for the loading indicator */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${getHttpMethodInfo(method).colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/**\n * Placeholder for the path field.\n *\n * Webhooks have no server to prefix the path, so the field doubles as the full\n * destination URL. Hint that a complete URL (including the host) is expected,\n * rather than a bare path.\n */\nconst pathPlaceholder = computed(() => {\n if (server) {\n return ''\n }\n return isWebhook\n ? translate('apiClient.addressBar.webhookUrlPlaceholder')\n : translate('apiClient.addressBar.urlPlaceholder')\n})\n\n/** Whether either dropdown (server or history) is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\n/** Uniquely identifies the currently selected operation + example */\nconst uniqueKey = computed(() =>\n getOperationExampleKey(method, path, exampleKey, documentSlug),\n)\n\n/** Clear conflict state when switching to a different operation */\nwatch(uniqueKey, () => {\n pathConflict.value = null\n methodConflict.value = null\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Focus helpers\n// ───────────────────────────────────────────────────────────────────\n\nconst handleFocusSendButton = (): void => {\n const desktop = sendButtonRef.value?.$el\n const mobile = mobileSendButtonRef.value?.$el\n\n // Focus whichever send button is currently visible\n if (desktop && desktop.offsetParent !== null) {\n desktop.focus()\n } else {\n mobile?.focus()\n }\n}\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n): void => {\n // On non-desktop layouts, if already focused we let the browser handle the\n // native behavior (e.g. selecting the browser's own address bar).\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path masking\n//\n// Drafts on `/` and auto-generated `/_scalar_temp...` paths are internal\n// placeholders — the address bar focuses and clears on mount and on\n// navigation so the user sees a blank prompt instead of the internal\n// path. The deferred mask only clears when CodeMirror still contains that\n// placeholder path, so a user edit that lands before the frame is preserved.\n// ───────────────────────────────────────────────────────────────────\n\nusePathMasking({\n isReady: () => addressBarRef.value?.codeMirror,\n operationKey: () => uniqueKey.value,\n shouldMask: () => isPlaceholderPath(path, documentSlug),\n // Defer to the next frame so focus() runs after click-handler side\n // effects that move focus (e.g. a dropdown refocusing its trigger),\n // which would otherwise blur our input and emit a spurious path\n // update against the now-empty value.\n onMask: () =>\n requestAnimationFrame(() => {\n const editorContent =\n addressBarRef.value?.codeMirror?.state.doc.toString()\n\n if (editorContent && editorContent !== path) {\n return\n }\n\n handleFocusAddressBar({ clear: true })\n }),\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Server extraction\n// ───────────────────────────────────────────────────────────────────\n\n/** If the path contains a server URL, extract it and select or add that server */\nconst extractAndSelectServer = (targetPath: string): string => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, remainingPath] = extracted\n\n // Server is already selected — nothing to do\n if (url === server?.url) {\n return remainingPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n if (matchingServer) {\n eventBus.emit('server:update:selected', { url, meta: serverMeta })\n } else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: { type: 'operation', path, method },\n })\n }\n\n return remainingPath\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path / method updates\n// ───────────────────────────────────────────────────────────────────\n\nconst normalizePath = (value: string): string =>\n value.startsWith('/') ? value : `/${value}`\n\n/** Emit a path/method update and reconcile conflicts + cursor state on the result */\nconst emitPathMethodUpdate = (\n targetMethod: string,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n if (isWebhook) {\n // A webhook has no OpenAPI server, so the field holds the full destination\n // URL. Keep exactly what the user entered — do not split it into an invisible\n // server plus a leftover path, and do not force a leading slash. That split\n // is what made a typed URL collapse to a stray \"/\" on blur.\n const webhookUrl = targetPath.trim()\n addressBarRef.value?.setCodeMirrorContent(webhookUrl)\n emit('update:webhook-url', webhookUrl)\n methodConflict.value = null\n pathConflict.value = null\n nextTick(() => refocusBlurTarget(blurTargetSelector))\n return\n }\n\n const extractedPath = extractAndSelectServer(targetPath)\n const normalizedPath = normalizePath(extractedPath)\n\n // Keep CodeMirror in sync so a conflict does not leave a stale value on screen\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, returnedSelector) => {\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n } else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n\n return\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path\n // unchanged. CodeMirror still shows the full URL, so force it back to\n // just the path.\n const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent\n if (\n status === 'no-change' &&\n mirrorContent &&\n mirrorContent !== extractedPath\n ) {\n addressBarRef.value?.setCodeMirrorContent(extractedPath)\n }\n\n nextTick(() => refocusBlurTarget(returnedSelector))\n },\n })\n}\n\n/** Change the operation's method, preferring the conflicting path if present */\nconst handleMethodChange = (newMethod: string): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Save the path on blur and replay the click that caused the blur (so e.g. a\n * click on the Send button still fires after the async update resolves).\n *\n * Tab-outs do not replay — tabbing to a button should not trigger its click\n * (`tabbedOut` is set on `@keydown.tab`).\n *\n * Programmatic focus moves also do not replay. On first navigation into a\n * draft operation, `usePathMasking` focuses and clears the address bar while\n * `ScalarSidebarNestedItems` focuses the drilled-in Back button in\n * `nextTick`. That steals focus from CodeMirror, fires blur with a\n * `relatedTarget` (the Back button), and without a guard we would treat it\n * like a user click: capture the selector, then `refocusBlurTarget` would\n * synthesize `.click()` on Back and navigate away.\n *\n * `FocusEvent.sourceCapabilities` is null when no input device caused the\n * focus change (e.g. `element.focus()`). A real pointer click sets it on the\n * blur that precedes the click, so we still replay Send and other buttons.\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget =\n event.relatedTarget instanceof Element ? event.relatedTarget : null\n const blurTargetSelector =\n tabbedOut.value ||\n ('sourceCapabilities' in event && event.sourceCapabilities === null)\n ? null\n : getSelector(relatedTarget)\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Save the path on Enter and trigger the Send button after the update resolves */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener from also handling this Enter press\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Unset the server when backspace is pressed on an empty path */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if (event.target instanceof HTMLElement && event.target.innerText === '\\n') {\n // A webhook has no server to unset; its full URL lives in the field itself.\n if (isWebhook) {\n return\n }\n eventBus.emit('server:update:selected', { url: '', meta: serverMeta })\n }\n}\n\nconst handleServerSelect = (\n payload: ApiReferenceEvents['server:update:selected'],\n): void => {\n eventBus.emit('server:update:selected', payload)\n}\n\nconst handleServerVariable = (\n payload: ApiReferenceEvents['server:update:variables'],\n): void => {\n eventBus.emit('server:update:variables', payload)\n}\n\n/** Address bar copy is handled in OperationBlock (same URL as Send). */\nconst requestCopyUrl = (): void => {\n eventBus.emit('copy-url:address-bar')\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Navigation\n// ───────────────────────────────────────────────────────────────────\n\nconst navigateToServersPage = (): void => {\n if (serverMeta.type === 'operation') {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n return\n }\n\n eventBus.emit('ui:navigate', { page: 'document', path: 'servers' })\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Lifecycle\n// ───────────────────────────────────────────────────────────────────\n\nconst unsubscribes: Array<() => void> = []\n\nonMounted(() => {\n unsubscribes.push(\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar),\n eventBus.on('ui:focus:send-button', handleFocusSendButton),\n eventBus.on('hooks:on:request:sent', startLoading),\n eventBus.on('hooks:on:request:complete', stopLoading),\n )\n})\n\nonBeforeUnmount(() => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n\n // Prevents the loading animation from continuing after unmount\n stopLoading()\n})\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <!--\n Address bar.\n\n The wide-container layout is a single row:\n `[Method | URL | Copy | History | Send]`. When the surrounding\n `@container` drops below `@3xl` only the send button moves to a\n second row, where it spans the full width. The method and copy\n controls stay inside the bar at every width so they keep belonging\n to the URL they act on rather than floating beside it.\n -->\n <div\n class=\"order-last flex h-auto w-full max-w-3xl grow-3 flex-wrap items-stretch [--scalar-address-bar-height:32px] @3xl:order-0 @3xl:flex-nowrap\">\n <!-- Address Bar -->\n <div\n :id=\"id\"\n class=\"address-bar-bg-states text-xxs group relative flex h-(--scalar-address-bar-height) w-full flex-1 flex-row items-stretch rounded-lg p-0.75\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n\n <!-- Only Send leaves the bar below `@3xl`; see the trailing row. -->\n <div class=\"flex\">\n <HttpMethod\n :isEditable=\"layout !== 'modal' && !isWebhook\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\"\n dir=\"ltr\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"isWebhook ? 'modal' : layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"handleServerSelect\"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"handleServerVariable\" />\n\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n :aria-label=\"translate('apiClient.addressBar.path')\"\n class=\"ml-1 min-w-fit pl-px outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal' && !isWebhook\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"pathPlaceholder\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1 flex\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"requestCopyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">{{\n translate('apiClient.addressBar.copyUrl')\n }}</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n {{\n translate('apiClient.addressBar.duplicateRequest', {\n method: (methodConflict ?? method).toUpperCase(),\n path: pathConflict ?? path,\n })\n }}\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative hidden h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold @3xl:flex\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs flex\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n\n <!--\n Below `@3xl` the send button moves out of the bar onto its own row\n and spans the full width, so the primary action stays easy to hit\n once the container is too narrow to hold it beside the URL.\n -->\n <ScalarButton\n ref=\"mobileSendButtonRef\"\n class=\"relative mt-2 h-(--scalar-address-bar-height) w-full shrink-0 justify-center overflow-hidden py-1 font-bold @3xl:hidden\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading || executionDisabled\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs\">{{\n translate('apiClient.addressBar.send')\n }}</span>\n </span>\n <span class=\"sr-only\"\n >{{\n translate('apiClient.addressBar.sendRequest', {\n method,\n url: `${server?.url ?? ''}${path}`,\n })\n }}\n </span>\n </ScalarButton>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n -ms-overflow-style: none;\n /* Fade the URL at the viewport edges. Mask lives on the scrollport (this\n element) rather than on `.cm-scroller`, whose width follows the URL's\n full content and pushes the right edge off-screen when overflowing. */\n mask-image: linear-gradient(\n to right,\n transparent 0,\n black 6px,\n black calc(100% - 24px),\n transparent 100%\n );\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":"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"}
|