@scalar/api-reference
Version:
Generate beautiful API references from OpenAPI documents
735 lines (734 loc) • 34.1 kB
JavaScript
import { AGENT_CONTEXT_SYMBOL, useAgent } from "../hooks/use-agent.js";
import AgentScalarButton_default from "./AgentScalar/AgentScalarButton.vue.js";
import AgentScalarDrawer_default from "./AgentScalar/AgentScalarDrawer.vue.js";
import OpenMCPButton_default from "./AgentScalar/OpenMCPButton.vue.js";
import ClassicHeader_default from "./ClassicHeader.vue.js";
import { useIntersection } from "../hooks/use-intersection.js";
import { createPluginManager } from "../plugins/plugin-manager.js";
import { PLUGIN_MANAGER_SYMBOL } from "../plugins/hooks/usePluginManager.js";
import { persistencePlugin } from "../plugins/persistance-plugin.js";
import { getIdFromUrl, makeUrlFromId, matchesBasePath } from "../helpers/id-routing.js";
import { addToPriorityQueue, blockIntersection, intersectionEnabled, scrollToLazy } from "../helpers/lazy-bus.js";
import Content_default from "./Content/Content.vue.js";
import MobileHeader_default from "./MobileHeader.vue.js";
import DeveloperTools_default from "../features/developer-tools/DeveloperTools.vue.js";
import DocumentSelector_default from "../features/multiple-documents/DocumentSelector.vue.js";
import SearchButton_default from "../features/Search/components/SearchButton.vue.js";
import { getSystemModePreference } from "../helpers/color-mode.js";
import { downloadDocument } from "../helpers/download.js";
import { loadAuthFromStorage, loadClientFromStorage } from "../helpers/load-from-perssistance.js";
import { mapConfigPlugins } from "../helpers/map-config-plugins.js";
import { mapConfigToWorkspaceStore } from "../helpers/map-config-to-workspace-store.js";
import { normalizeConfigurations } from "../helpers/normalize-configurations.js";
import { safeDeepClone } from "../helpers/safe-deep-clone.js";
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, guardReactiveProps, normalizeClass, normalizeProps, onBeforeMount, onBeforeUnmount, onMounted, onServerPrefetch, openBlock, provide, ref, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useTemplateRef, watch, withCtx } from "vue";
import { provideUseId } from "@headlessui/vue";
import { OpenApiClientButton } from "@scalar/api-client/blocks/operation-block";
import { createApiClientModal } from "@scalar/api-client/modal";
import { ScalarColorModeToggleButton, ScalarColorModeToggleIcon, ScalarSidebarFooter, addScalarClassesToHeadless } from "@scalar/components";
import { isLocalUrl } from "@scalar/helpers/url/is-local-url";
import { ScalarSidebar, createSidebarState, scrollSidebarToTop } from "@scalar/sidebar";
import { getThemeStyles, hasObtrusiveScrollbars } from "@scalar/themes";
import { apiReferenceConfigurationSchema } from "@scalar/types/api-reference";
import { useClipboard } from "@scalar/use-hooks/useClipboard";
import { useColorMode } from "@scalar/use-hooks/useColorMode";
import { ScalarToasts } from "@scalar/use-toasts";
import { createWorkspaceStore } from "@scalar/workspace-store/client";
import { createWorkspaceEventBus } from "@scalar/workspace-store/events";
import { getActiveEnvironment, getServers } from "@scalar/workspace-store/request-example";
import { useScrollLock } from "@vueuse/core";
import diff from "microdiff";
//#region src/components/ApiReference.vue?vue&type=script&setup=true&lang.ts
var _hoisted_1 = {
key: 1,
class: "flex gap-1.5 px-3 pt-3"
};
var _hoisted_2 = { key: 1 };
var _hoisted_3 = ["aria-label", "inert"];
var _hoisted_4 = { class: "w-64 empty:hidden" };
var _hoisted_5 = {
key: 2,
class: "references-footer"
};
var version = "1.55.3";
if (typeof window !== "undefined") console.info(`@scalar/api-reference@${version}`);
var ApiReference_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
__name: "ApiReference",
props: { configuration: {} },
setup(__props, { expose: __expose }) {
const props = __props;
const { copyToClipboard } = useClipboard();
/**
* Used to inject the environment into built packages
*
* Primary use case is the open-in-client button
*/
const isDevelopment = false;
const obtrusiveScrollbars = computed(hasObtrusiveScrollbars);
const eventBus = createWorkspaceEventBus({ debug: isDevelopment });
const isSidebarOpen = ref(false);
/**
* Due to a bug in headless UI, we need to set an ID here that can be shared across server/client
* TODO remove this once the bug is fixed
*
* @see https://github.com/tailwindlabs/headlessui/issues/2979
*/
provideUseId(() => useId());
/**
* Configuration Handling
*
* We will normalize the configurations and store them in a computed property.
* The active configuration will be associated with the active document.
*/
const configList = computed(() => normalizeConfigurations(props.configuration));
const isMultiDocument = computed(() => Object.keys(configList.value).length > 1);
/** Search for the source with a default attribute or use the first one */
const activeSlug = ref(Object.values(configList.value).find((c) => c.default)?.slug ?? configList.value[Object.keys(configList.value)?.[0] ?? ""]?.slug ?? "");
/**
* On initial page load we need to determine if there is a valid document slug in the URL
*
* If there is we set the active slug to the document slug
*/
if (typeof window !== "undefined") {
const url = new URL(window.location.href);
const apiParam = url.searchParams.get("api");
if (apiParam && configList.value[apiParam]) {
activeSlug.value = apiParam;
const newUrl = makeUrlFromId(getIdFromUrl(url, configList.value[apiParam].config.pathRouting?.basePath, apiParam), configList.value[apiParam].config.pathRouting?.basePath, isMultiDocument.value);
if (newUrl) {
newUrl.searchParams.delete("api");
window.history.replaceState({}, "", newUrl.toString());
}
}
const documentSlug = getIdFromUrl(url, Object.values(configList.value).map((c) => c.config.pathRouting?.basePath).find((p) => p ? matchesBasePath(url, p) : false), isMultiDocument.value ? void 0 : activeSlug.value).split("/")[0];
if (documentSlug && configList.value[documentSlug]) activeSlug.value = documentSlug;
}
/** Computed document options list for the selector logic */
const documentOptionList = computed(() => Object.values(configList.value).map((c) => ({
label: c.title,
id: c.slug
})));
/** Configuration overrides to apply to the selected document (from the localhost toolbar) */
const configurationOverrides = ref({});
/** Any dev toolbar modifications are merged with the active configuration */
const mergedConfig = computed(() => ({
...apiReferenceConfigurationSchema.parse({}),
...configList.value[activeSlug.value]?.config,
...configurationOverrides.value
}));
/** Convenience break out var to determine which routing mode we are using */
const basePath = computed(() => mergedConfig.value.pathRouting?.basePath);
const themeStyle = computed(() => getThemeStyles(mergedConfig.value.theme, { fonts: mergedConfig.value.withDefaultFonts }));
/** Plugin injection is not reactive. All plugins must be provided at first render */
const pluginManager = createPluginManager({ plugins: Object.values(configList.value).flatMap((c) => c.config.plugins ?? []) });
provide(PLUGIN_MANAGER_SYMBOL, pluginManager);
pluginManager.notifyInit(mergedConfig.value);
watch(mergedConfig, (config) => pluginManager.notifyConfigChange(config));
/** Navigation State Handling */
if (mergedConfig.value.redirect && typeof window !== "undefined") {
const newPath = mergedConfig.value.redirect((mergedConfig.value.pathRouting ? window.location.pathname : "") + window.location.hash);
if (newPath) window.history.replaceState({}, "", newPath);
}
/**
* Sets the active slug and updates the URL with the selected document slug
*
* If an element ID is passed in we will configure the path or hash routing
*/
function syncSlugAndUrlWithDocument(slug, elementId, config) {
const url = makeUrlFromId(elementId || slug, config.pathRouting?.basePath, isMultiDocument.value);
if (url) window.history.replaceState({}, "", url.toString());
activeSlug.value = slug;
}
/** Workspace Store Initialization */
/**
* Initializes the new client workspace store.
*/
const workspaceStore = createWorkspaceStore({ verbose: isDevelopment });
/**
* We need to keep the client store separate from the workspace store
* This is because we want the client store to be a playground where users can test out their requests without affecting the references store
*/
const clientStore = createWorkspaceStore({
verbose: isDevelopment,
plugins: [persistencePlugin({
prefix: () => activeSlug.value,
persistAuth: () => mergedConfig.value.persistAuth ?? false
})]
});
const { toggleColorMode, isDarkMode } = useColorMode({
initialColorMode: {
true: "dark",
false: "light",
undefined: "system"
}[String(mergedConfig.value.darkMode)],
overrideColorMode: mergedConfig.value.forceDarkModeState
});
/** Initialize the sidebar */
const sidebarState = createSidebarState(computed(() => {
return Object.entries(workspaceStore.workspace.documents).map(([slug, document]) => ({
id: slug,
type: "document",
description: document.info.description,
name: document.info.title ?? slug,
title: document.info.title ?? slug,
children: document?.["x-scalar-navigation"]?.children ?? []
}));
}), { hooks: {} });
/** Recursively set all children of the given items to open */
const setChildrenOpen = (items) => {
items.forEach((item) => {
if (item.type === "tag" || item.type === "models") sidebarState.setExpanded(item.id, true);
if ("children" in item && item.children) setChildrenOpen(item.children);
});
};
/** We get the sub items for the sidebar based on the configuration/document slug */
const sidebarItems = computed(() => {
const config = mergedConfig.value;
if (!config) return [];
const docItems = sidebarState.items.value.find((item) => item.id === activeSlug.value)?.children ?? [];
if (config.defaultOpenAllTags) setChildrenOpen(docItems);
if (config.expandAllModelSections) {
const models = docItems.find((item) => item.type === "models");
if (models) {
sidebarState.setExpanded(models.id, true);
models.children?.forEach((child) => {
sidebarState.setExpanded(child.id, true);
});
}
}
return docItems;
});
/** Find the sidebar entry that represents the introduction section */
const infoSectionId = computed(() => sidebarItems.value.find((item) => item.type === "text" && item.title === "Introduction")?.id ?? `${activeSlug.value}/description/introduction`);
/** User for mobile navigation */
const breadcrumb = ref("");
const slotProps = computed(() => ({ breadcrumb: breadcrumb.value }));
const setBreadcrumb = (id) => {
const item = sidebarState.getEntryById(id);
if (!item || item.type === "document") breadcrumb.value = "";
else breadcrumb.value = item.title;
};
const scrollToLazyElement = (id) => {
setBreadcrumb(id);
sidebarState.setSelected(id);
scrollToLazy(id, sidebarState.setExpanded, sidebarState.getEntryById);
};
/** Maps some config values to the workspace store to keep it reactive */
mapConfigToWorkspaceStore({
config: () => mergedConfig.value,
store: workspaceStore,
isDarkMode
});
mapConfigToWorkspaceStore({
config: () => mergedConfig.value,
store: clientStore,
isDarkMode
});
/** Merged environment variables from workspace and document levels */
const environment = computed(() => getActiveEnvironment(workspaceStore, workspaceStore.workspace.activeDocument ?? null).environment);
if (typeof window !== "undefined") window.dataDumpWorkspace = () => workspaceStore;
__expose({
eventBus,
workspaceStore,
sidebarItems
});
/**
* Computes a mapping from model names to their sidebar entry IDs.
* This is used for quick lookups and navigation within the sidebar.
*/
const modelsIndex = computed(() => {
return sidebarItems.value.filter((item) => item.type === "models").flatMap((item) => item.children ?? []).filter((item) => item.type === "model").reduce((acc, item) => {
acc[item.name] = item.id;
return acc;
}, {});
});
eventBus.on("scroll-to:model-by-name", ({ name }) => {
/** Find the model in the models index */
const model = modelsIndex.value[name];
if (model) scrollToLazyElement(model);
});
const addDocument = async (input, navigationOptions) => {
const result = await workspaceStore.addDocument(input, navigationOptions);
const state = workspaceStore.exportWorkspace();
clientStore.loadWorkspace({
auth: {},
documents: { [input.name]: safeDeepClone(state.documents[input.name]) ?? {
"openapi": "3.1.0",
"info": {
title: "",
version: ""
},
"x-scalar-original-document-hash": ""
} },
intermediateDocuments: {},
originalDocuments: {},
overrides: {},
history: {},
meta: {}
});
return result;
};
/**
* Handle changing the active document
*
* 1. If the document has not be loaded to the workspace store we set it to empty and asynchronously load it
* 2. If the document has been loaded to the workspace store we just set it to active
* 3. If the content from the configuration has changes we need to update the document in the workspace store
*/
const changeSelectedDocument = async (slug, elementId) => {
const normalized = configList.value[slug];
if (!normalized) {
console.warn(`Document ${slug} not found in configList`);
return;
}
const config = {
...normalized.config,
...configurationOverrides.value
};
const onDocumentSelectPromise = config.onDocumentSelect?.();
syncSlugAndUrlWithDocument(slug, elementId, config);
apiClient.value?.route({
documentSlug: slug,
method: "get",
path: "/"
});
if (!workspaceStore.workspace.documents[slug]) {
const result = await addDocument(normalized.source.url ? {
name: slug,
url: normalized.source.url,
fetch: config.fetch
} : {
name: slug,
document: normalized.source.content ?? {}
}, config);
const document = clientStore.workspace.documents[slug];
if (result === true && document !== void 0 && document["x-scalar-selected-server"] === void 0) {
const servers = getServers(normalized.config.servers ?? document.servers, {
baseServerUrl: mergedConfig.value.baseServerURL,
documentUrl: normalized.source.url
});
if (servers.length > 0) clientStore.updateDocument(slug, "x-scalar-selected-server", servers[0].url);
}
}
workspaceStore.update("x-scalar-active-document", slug);
clientStore.update("x-scalar-active-document", slug);
if (config.persistAuth) loadAuthFromStorage(clientStore, slug);
(async () => {
await onDocumentSelectPromise;
config.onLoaded?.(slug);
})();
if (elementId && elementId !== slug) scrollToLazyElement(elementId);
else if (config.defaultOpenFirstTag) {
const firstTag = sidebarItems.value.find((item) => item.type === "tag");
if (firstTag) sidebarState.setExpanded(firstTag.id, true);
}
};
/**
* TODO:Move this to a dedicated updateDocument function in the future and
* away from vue-reactivity based updates
*/
watch(() => Object.values(configList.value), async (newConfigList, oldConfigList) => {
/**
* Handles replacing and updating documents within the workspace store
* when we detect configuration changes.
*/
const updateSource = async (updated, previous) => {
/** If we have not loaded the document previously we don't need to handle any updates to store */
if (!workspaceStore.workspace.documents[updated.slug]) return;
/** If the URL has changed we fetch and rebase */
if (updated.source.url && updated.source.url !== previous?.source.url) {
await addDocument({
name: updated.slug,
url: updated.source.url,
fetch: updated.config.fetch
}, updated.config);
return;
}
if (!updated.source.content) return;
/**
* We need to deeply check for document changes. Parse documents and then only rebase
* if we detect deep changes in the two sources
*/
if (diff(updated.source.content, previous && "content" in previous.source ? previous.source.content ?? {} : {}).length) await addDocument({
name: updated.slug,
document: updated.source.content
}, updated.config);
};
newConfigList.forEach((newConfig, index) => updateSource(newConfig, oldConfigList[index]));
const newSlugs = newConfigList.map((c) => c.slug);
const oldSlugs = oldConfigList.map((c) => c.slug);
if (newSlugs.length !== oldSlugs.length || !newSlugs.every((slug, index) => slug === oldSlugs[index])) await changeSelectedDocument(newSlugs[0] ?? "");
}, { deep: true });
/** Preload the first document during SSR */
onServerPrefetch(() => changeSelectedDocument(activeSlug.value));
/** Load the first document on page load */
onBeforeMount(async () => {
loadClientFromStorage(clientStore);
await changeSelectedDocument(activeSlug.value, getIdFromUrl(window.location.href, configList.value[activeSlug.value]?.config.pathRouting?.basePath, isMultiDocument.value ? void 0 : activeSlug.value));
});
const documentUrl = computed(() => {
return configList.value[activeSlug.value]?.source?.url;
});
/**
* Determines if Agent Scalar should be enabled based on the configuration and the current URL
*
* - If the agent is disabled in the configuration, it should not be enabled
* - If the current URL is a local URL, it should be enabled
* - If the agent key is set, it should be enabled
*/
const agent = useAgent({ agentEnabled: computed(() => {
if (configList.value[activeSlug.value]?.agent?.disabled) return false;
if (typeof window !== "undefined" && isLocalUrl(window.location.href)) return true;
return Boolean(configList.value[activeSlug.value]?.agent?.key);
}) });
provide(AGENT_CONTEXT_SYMBOL, agent);
const modal = useTemplateRef("modal");
const apiClient = ref(null);
onMounted(() => {
if (!modal.value) return;
apiClient.value = createApiClientModal({
el: modal.value,
eventBus,
workspaceStore: clientStore,
options: mergedConfig,
plugins: [...pluginManager.getApiClientPlugins(), ...mapConfigPlugins(mergedConfig, environment)]
});
});
onBeforeUnmount(() => {
pluginManager.notifyDestroy();
apiClient.value?.app.unmount();
});
/** Ensure we call the onServerChange callback */
eventBus.on("server:update:selected", ({ url }) => mergedConfig.value.onServerChange?.(url));
/** Download the document from the store */
eventBus.on("ui:download:document", ({ format }) => {
const document = workspaceStore.exportActiveDocument(format);
if (!document) {
console.error("No document found to download");
return;
}
downloadDocument(document, activeSlug.value ?? "openapi", format);
});
/**
* Handler for a direct navigation event such as a sidebar or search click
*
* Depending on the item type we handle a selection event differently:
*
* - Tag: If a tag is closed we open it and all its parents and scroll to it
* If a tag is open we just close the tag
* - Operation:
* Open all parents and scroll to the operation
*/
const handleSelectSidebarEntry = (id, caller) => {
const item = sidebarState.getEntryById(id);
if ((item?.type === "tag" || item?.type === "models" || item?.type === "text") && sidebarState.isExpanded(id) && sidebarState.selectedItem.value === id) {
const unblock = blockIntersection();
sidebarState.setExpanded(id, false);
unblock();
return;
}
if (item?.type !== "tag" && item?.type !== "models") isSidebarOpen.value = false;
scrollToLazyElement(id);
const url = makeUrlFromId(id, basePath.value, isMultiDocument.value);
if (url) {
window.history.pushState({}, "", url);
if (caller === "sidebar") mergedConfig.value.onSidebarClick?.(url.toString());
}
if (agent.showAgent.value) agent.closeAgent();
};
/** Handle a navigation item selection event */
eventBus.on("select:nav-item", ({ id }) => handleSelectSidebarEntry(id));
/** Handle a scroll to navigation item event */
eventBus.on("scroll-to:nav-item", ({ id }) => handleSelectSidebarEntry(id));
/** Handle an intersecting navigation item event */
eventBus.on("intersecting:nav-item", ({ id }) => {
if (!intersectionEnabled.value) return;
sidebarState.setSelected(id);
setBreadcrumb(id);
scrollSidebarToTop(id);
const url = makeUrlFromId(id, basePath.value, isMultiDocument.value);
if (url && workspaceStore.workspace.activeDocument) window.history.replaceState({}, "", url.toString());
});
eventBus.on("toggle:nav-item", ({ id, open }) => {
if (open) {
mergedConfig.value.onShowMore?.(id);
const entry = sidebarState.getEntryById(id);
if (entry && "children" in entry && entry.children) {
const first = entry.children[0];
if (first) addToPriorityQueue(first.id);
}
}
sidebarState.setExpanded(id, open ?? !sidebarState.isExpanded(id));
});
eventBus.on("copy-url:nav-item", ({ id }) => {
const url = makeUrlFromId(id, basePath.value, isMultiDocument.value)?.toString();
return url && copyToClipboard(url);
});
onBeforeMount(() => {
window.history.scrollRestoration = "manual";
addScalarClassesToHeadless();
window.addEventListener("popstate", () => {
const id = getIdFromUrl(window.location.href, mergedConfig.value.pathRouting?.basePath, isMultiDocument.value ? void 0 : activeSlug.value);
if (id) scrollToLazyElement(id);
});
});
const documentStartRef = useTemplateRef("documentStartRef");
/**
* Uses `immediate` so the sentinel fires as soon as it enters the viewport (not just at the center strip).
* When the user scrolls away from the top, both this observer and the first section's center-strip
* observer are intersecting simultaneously, so the section observer does not re-fire on its own.
* The `onExit` callback bridges that gap by finding whichever section is at the viewport center
* and re-emitting the nav event for it.
*/
useIntersection(documentStartRef, () => {
eventBus.emit("intersecting:nav-item", { id: activeSlug.value });
}, {
onExit: () => {
const centerY = window.innerHeight / 2;
const section = document.elementsFromPoint(window.innerWidth / 2, centerY).find((el) => el.tagName === "SECTION" && el.id);
if (section?.id) eventBus.emit("intersecting:nav-item", { id: section.id });
},
immediate: true
});
const colorMode = computed(() => {
const mode = workspaceStore.workspace["x-scalar-color-mode"];
if (mode === "system") return getSystemModePreference();
return mode;
});
const bodyScrollLocked = useScrollLock(typeof document !== "undefined" ? document.body : null);
watch(agent.showAgent, () => bodyScrollLocked.value = agent.showAgent.value);
const showMCPButton = computed(() => {
if (mergedConfig.value.mcp?.disabled) return false;
if (typeof window !== "undefined" && isLocalUrl(window.location.href)) return true;
if (mergedConfig.value.mcp) return true;
return false;
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock("div", null, [
(openBlock(), createBlock(resolveDynamicComponent("style"), null, {
default: withCtx(() => [createTextVNode(toDisplayString(mergedConfig.value.customCss) + " " + toDisplayString(themeStyle.value), 1)]),
_: 1
})),
createElementVNode("div", {
ref: "documentEl",
class: normalizeClass(["scalar-app scalar-api-reference references-layout", [{
"scalar-api-references-standalone-mobile": mergedConfig.value.showSidebar,
"scalar-scrollbars-obtrusive": obtrusiveScrollbars.value,
"references-editable": mergedConfig.value.isEditable,
"references-sidebar": mergedConfig.value.showSidebar,
"references-sidebar-mobile-open": isSidebarOpen.value,
"references-classic": mergedConfig.value.layout === "classic"
}, _ctx.$attrs.class]])
}, [
unref(agent).agentEnabled.value ? (openBlock(), createBlock(unref(AgentScalarDrawer_default), {
key: 0,
agentScalarConfiguration: configList.value[activeSlug.value]?.agent,
externalUrls: mergedConfig.value.externalUrls,
workspaceStore: unref(workspaceStore)
}, null, 8, [
"agentScalarConfiguration",
"externalUrls",
"workspaceStore"
])) : createCommentVNode("", true),
mergedConfig.value.layout === "modern" ? (openBlock(), createBlock(MobileHeader_default, {
key: 1,
breadcrumb: breadcrumb.value,
isSidebarOpen: isSidebarOpen.value,
showSidebar: mergedConfig.value.showSidebar,
onToggleSidebar: _cache[3] || (_cache[3] = () => isSidebarOpen.value = !isSidebarOpen.value)
}, {
search: withCtx(() => [!mergedConfig.value.hideSearch ? (openBlock(), createBlock(SearchButton_default, {
key: 0,
class: "my-2",
document: unref(workspaceStore).workspace.activeDocument,
eventBus: unref(eventBus),
hideModels: mergedConfig.value.hideModels,
searchHotKey: mergedConfig.value.searchHotKey,
showSidebar: mergedConfig.value.showSidebar
}, null, 8, [
"document",
"eventBus",
"hideModels",
"searchHotKey",
"showSidebar"
])) : createCommentVNode("", true)]),
sidebar: withCtx(({ sidebarClasses }) => [mergedConfig.value.showSidebar && mergedConfig.value.layout === "modern" ? (openBlock(), createBlock(unref(ScalarSidebar), {
key: 0,
"aria-label": `Sidebar for ${unref(workspaceStore).workspace.activeDocument?.info?.title}`,
class: normalizeClass(["t-doc__sidebar", sidebarClasses]),
isExpanded: unref(sidebarState).isExpanded,
isSelected: unref(sidebarState).isSelected,
items: sidebarItems.value,
layout: "reference",
options: mergedConfig.value,
role: "navigation",
onSelectItem: _cache[1] || (_cache[1] = (id) => handleSelectSidebarEntry(id, "sidebar")),
onToggleGroup: _cache[2] || (_cache[2] = (id) => unref(sidebarState).setExpanded(id, !unref(sidebarState).isExpanded(id)))
}, {
header: withCtx(() => [
documentOptionList.value.length > 1 ? (openBlock(), createBlock(DocumentSelector_default, {
key: 0,
modelValue: activeSlug.value,
options: documentOptionList.value,
"onUpdate:modelValue": changeSelectedDocument
}, null, 8, ["modelValue", "options"])) : createCommentVNode("", true),
!mergedConfig.value.hideSearch ? (openBlock(), createElementBlock("div", _hoisted_1, [createVNode(SearchButton_default, {
document: unref(workspaceStore).workspace.activeDocument,
eventBus: unref(eventBus),
hideModels: mergedConfig.value.hideModels,
searchHotKey: mergedConfig.value.searchHotKey
}, null, 8, [
"document",
"eventBus",
"hideModels",
"searchHotKey"
]), unref(agent).agentEnabled.value ? (openBlock(), createBlock(unref(AgentScalarButton_default), { key: 0 })) : createCommentVNode("", true)])) : createCommentVNode("", true),
renderSlot(_ctx.$slots, "sidebar-start", normalizeProps(guardReactiveProps(slotProps.value)), void 0, true)
]),
footer: withCtx(() => [renderSlot(_ctx.$slots, "sidebar-end", normalizeProps(guardReactiveProps(slotProps.value)), () => [createVNode(unref(ScalarSidebarFooter), { class: "darklight-reference" }, {
toggle: withCtx(() => [!mergedConfig.value.hideDarkModeToggle && !mergedConfig.value.forceDarkModeState ? (openBlock(), createBlock(unref(ScalarColorModeToggleButton), {
key: 0,
modelValue: colorMode.value === "dark",
"onUpdate:modelValue": _cache[0] || (_cache[0] = () => unref(toggleColorMode)())
}, null, 8, ["modelValue"])) : (openBlock(), createElementBlock("span", _hoisted_2))]),
default: withCtx(() => [!mergedConfig.value.hideClientButton && !showMCPButton.value ? (openBlock(), createBlock(unref(OpenApiClientButton), {
key: 0,
buttonSource: "sidebar",
integration: mergedConfig.value._integration,
isDevelopment: unref(isDevelopment),
url: documentUrl.value
}, null, 8, [
"integration",
"isDevelopment",
"url"
])) : createCommentVNode("", true), showMCPButton.value ? (openBlock(), createBlock(unref(OpenMCPButton_default), {
key: 1,
config: mergedConfig.value.mcp,
externalUrls: mergedConfig.value.externalUrls,
isDevelopment: unref(isDevelopment),
url: documentUrl.value,
workspace: unref(workspaceStore)
}, null, 8, [
"config",
"externalUrls",
"isDevelopment",
"url",
"workspace"
])) : createCommentVNode("", true)]),
_: 1
})], true)]),
_: 3
}, 8, [
"aria-label",
"class",
"isExpanded",
"isSelected",
"items",
"options"
])) : createCommentVNode("", true)]),
_: 3
}, 8, [
"breadcrumb",
"isSidebarOpen",
"showSidebar"
])) : createCommentVNode("", true),
createElementVNode("main", {
"aria-label": `Open API Documentation for ${unref(workspaceStore).workspace.activeDocument?.info?.title}`,
class: "references-rendered",
inert: unref(agent).showAgent.value
}, [createVNode(Content_default, {
authStore: unref(clientStore).auth,
clientDocument: unref(clientStore).workspace.activeDocument,
document: unref(workspaceStore).workspace.activeDocument,
environment: environment.value,
eventBus: unref(eventBus),
expandedItems: unref(sidebarState).expandedItems.value,
headingSlugGenerator: mergedConfig.value.generateHeadingSlug ?? ((heading) => `${activeSlug.value}/description/${heading.slug}`),
infoSectionId: infoSectionId.value,
items: sidebarItems.value,
options: mergedConfig.value,
xScalarDefaultClient: unref(clientStore).workspace["x-scalar-default-client"]
}, createSlots({
start: withCtx(() => [
createElementVNode("div", {
ref_key: "documentStartRef",
ref: documentStartRef
}, null, 512),
unref(workspaceStore).workspace.activeDocument ? (openBlock(), createBlock(unref(DeveloperTools_default), {
key: 0,
overrides: configurationOverrides.value,
"onUpdate:overrides": _cache[4] || (_cache[4] = ($event) => configurationOverrides.value = $event),
class: "references-developer-tools",
configuration: mergedConfig.value,
externalUrls: mergedConfig.value.externalUrls,
workspace: unref(workspaceStore)
}, null, 8, [
"overrides",
"configuration",
"externalUrls",
"workspace"
])) : createCommentVNode("", true),
mergedConfig.value.layout === "classic" ? (openBlock(), createBlock(ClassicHeader_default, { key: 1 }, {
"dark-mode-toggle": withCtx(() => [!mergedConfig.value.hideDarkModeToggle && !mergedConfig.value.forceDarkModeState ? (openBlock(), createBlock(unref(ScalarColorModeToggleIcon), {
key: 0,
class: "text-c-2 hover:text-c-1",
mode: colorMode.value,
style: { "transform": "scale(1.4)" },
variant: "icon",
onClick: _cache[5] || (_cache[5] = () => unref(toggleColorMode)())
}, null, 8, ["mode"])) : createCommentVNode("", true)]),
default: withCtx(() => [createElementVNode("div", _hoisted_4, [documentOptionList.value.length > 1 ? (openBlock(), createBlock(DocumentSelector_default, {
key: 0,
modelValue: activeSlug.value,
options: documentOptionList.value,
"onUpdate:modelValue": changeSelectedDocument
}, null, 8, ["modelValue", "options"])) : createCommentVNode("", true)]), !mergedConfig.value.hideSearch ? (openBlock(), createBlock(SearchButton_default, {
key: 0,
class: "t-doc__sidebar max-w-64",
document: unref(workspaceStore).workspace.activeDocument,
eventBus: unref(eventBus),
hideModels: mergedConfig.value.hideModels,
searchHotKey: mergedConfig.value.searchHotKey
}, null, 8, [
"document",
"eventBus",
"hideModels",
"searchHotKey"
])) : createCommentVNode("", true)]),
_: 1
})) : createCommentVNode("", true),
renderSlot(_ctx.$slots, "content-start", normalizeProps(guardReactiveProps(slotProps.value)), void 0, true)
]),
end: withCtx(() => [renderSlot(_ctx.$slots, "content-end", normalizeProps(guardReactiveProps(slotProps.value)), void 0, true)]),
_: 2
}, [mergedConfig.value.isEditable ? {
name: "empty-state",
fn: withCtx(() => [renderSlot(_ctx.$slots, "editor-placeholder", normalizeProps(guardReactiveProps(slotProps.value)), void 0, true)]),
key: "0"
} : void 0]), 1032, [
"authStore",
"clientDocument",
"document",
"environment",
"eventBus",
"expandedItems",
"headingSlugGenerator",
"infoSectionId",
"items",
"options",
"xScalarDefaultClient"
])], 8, _hoisted_3),
_ctx.$slots.footer ? (openBlock(), createElementBlock("div", _hoisted_5, [renderSlot(_ctx.$slots, "footer", normalizeProps(guardReactiveProps(slotProps.value)), void 0, true)])) : createCommentVNode("", true),
createElementVNode("div", {
ref_key: "modal",
ref: modal
}, null, 512)
], 2),
createVNode(unref(ScalarToasts))
]);
};
}
});
//#endregion
export { ApiReference_vue_vue_type_script_setup_true_lang_default as default };
//# sourceMappingURL=ApiReference.vue.script.js.map