@scalar/api-client 3.2.2 → 3.3.1
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 +17 -0
- package/dist/style.css +122 -146
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +9 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.d.ts +2 -0
- 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 +20 -10
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTable.vue.d.ts +2 -2
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.d.ts +2 -2
- package/dist/v2/blocks/response-block/components/ResponseEmpty.vue.d.ts +2 -2
- package/dist/v2/components/modals/ModalClientContainer.vue.d.ts +2 -2
- 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 +17 -24
- package/dist/v2/components/modals/ModalClientContainer.vue.script.js.map +1 -1
- package/dist/v2/components/server/ServerDropdown.vue.d.ts +2 -2
- package/dist/v2/components/sidebar/Sidebar.vue.script.js +2 -2
- package/dist/v2/components/sidebar/Sidebar.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/app/App.vue.d.ts +22 -4
- package/dist/v2/features/app/App.vue.d.ts.map +1 -1
- package/dist/v2/features/app/App.vue.js.map +1 -1
- package/dist/v2/features/app/App.vue.script.js +57 -54
- package/dist/v2/features/app/App.vue.script.js.map +1 -1
- package/dist/v2/features/app/app-events.d.ts.map +1 -1
- package/dist/v2/features/app/app-events.js +4 -0
- package/dist/v2/features/app/app-events.js.map +1 -1
- package/dist/v2/features/app/app-state.d.ts +6 -0
- package/dist/v2/features/app/app-state.d.ts.map +1 -1
- package/dist/v2/features/app/app-state.js +15 -1
- package/dist/v2/features/app/app-state.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.d.ts +20 -0
- package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/AppHeader.vue.js +7 -0
- package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -0
- package/dist/v2/features/app/components/AppHeader.vue.script.js +43 -0
- package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -0
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts +17 -56
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.script.js +437 -271
- package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
- package/dist/v2/features/app/helpers/load-registry-document.d.ts +18 -0
- package/dist/v2/features/app/helpers/load-registry-document.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/load-registry-document.js +45 -0
- package/dist/v2/features/app/helpers/load-registry-document.js.map +1 -0
- package/dist/v2/features/app/helpers/routes.d.ts +6 -1
- package/dist/v2/features/app/helpers/routes.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/routes.js +84 -75
- package/dist/v2/features/app/helpers/routes.js.map +1 -1
- package/dist/v2/features/app/hooks/use-document-filter.d.ts +38 -0
- package/dist/v2/features/app/hooks/use-document-filter.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-document-filter.js +63 -0
- package/dist/v2/features/app/hooks/use-document-filter.js.map +1 -0
- package/dist/v2/features/app/hooks/use-sidebar-context-menu.d.ts +17258 -0
- package/dist/v2/features/app/hooks/use-sidebar-context-menu.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-sidebar-context-menu.js +107 -0
- package/dist/v2/features/app/hooks/use-sidebar-context-menu.js.map +1 -0
- package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts +95 -0
- package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-sidebar-documents.js +97 -0
- package/dist/v2/features/app/hooks/use-sidebar-documents.js.map +1 -0
- package/dist/v2/features/app/index.d.ts +1 -0
- package/dist/v2/features/app/index.d.ts.map +1 -1
- package/dist/v2/features/collection/components/GetStarted.vue.d.ts +13 -0
- package/dist/v2/features/collection/components/GetStarted.vue.d.ts.map +1 -0
- package/dist/v2/features/collection/components/GetStarted.vue.js +7 -0
- package/dist/v2/features/collection/components/GetStarted.vue.js.map +1 -0
- package/dist/v2/features/collection/components/GetStarted.vue.script.js +101 -0
- package/dist/v2/features/collection/components/GetStarted.vue.script.js.map +1 -0
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.d.ts +4 -0
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.d.ts.map +1 -1
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.js +5 -1
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.js.map +1 -1
- package/dist/v2/features/search/components/DocumentSearchModal.vue.d.ts +16 -0
- package/dist/v2/features/search/components/DocumentSearchModal.vue.d.ts.map +1 -0
- package/dist/v2/features/search/components/DocumentSearchModal.vue.js +9 -0
- package/dist/v2/features/search/components/DocumentSearchModal.vue.js.map +1 -0
- package/dist/v2/features/search/components/DocumentSearchModal.vue.script.js +123 -0
- package/dist/v2/features/search/components/DocumentSearchModal.vue.script.js.map +1 -0
- package/dist/v2/features/search/components/SearchResult.vue.d.ts +11 -0
- package/dist/v2/features/search/components/SearchResult.vue.d.ts.map +1 -0
- package/dist/v2/features/search/components/SearchResult.vue.js +7 -0
- package/dist/v2/features/search/components/SearchResult.vue.js.map +1 -0
- package/dist/v2/features/search/components/SearchResult.vue.script.js +71 -0
- package/dist/v2/features/search/components/SearchResult.vue.script.js.map +1 -0
- package/dist/v2/features/search/hooks/use-document-search.d.ts +19 -0
- package/dist/v2/features/search/hooks/use-document-search.d.ts.map +1 -0
- package/dist/v2/features/search/hooks/use-document-search.js +42 -0
- package/dist/v2/features/search/hooks/use-document-search.js.map +1 -0
- package/dist/v2/features/search/index.d.ts +2 -0
- package/dist/v2/features/search/index.d.ts.map +1 -1
- package/dist/v2/features/search/index.js +3 -1
- package/dist/v2/helpers/handle-hotkeys.d.ts.map +1 -1
- package/dist/v2/helpers/handle-hotkeys.js +8 -4
- package/dist/v2/helpers/handle-hotkeys.js.map +1 -1
- package/dist/v2/types/configuration.d.ts +1 -0
- package/dist/v2/types/configuration.d.ts.map +1 -1
- package/package.json +14 -13
- package/dist/assets/rabbit.ascii.virtual.js +0 -6
- package/dist/assets/rabbit.ascii.virtual.js.map +0 -1
- package/dist/assets/rabbitjump.ascii.virtual.js +0 -6
- package/dist/assets/rabbitjump.ascii.virtual.js.map +0 -1
- package/dist/v2/features/app/components/DownloadAppButton.vue.d.ts +0 -4
- package/dist/v2/features/app/components/DownloadAppButton.vue.d.ts.map +0 -1
- package/dist/v2/features/app/components/DownloadAppButton.vue.js +0 -9
- package/dist/v2/features/app/components/DownloadAppButton.vue.js.map +0 -1
- package/dist/v2/features/app/components/DownloadAppButton.vue.script.js +0 -21
- package/dist/v2/features/app/components/DownloadAppButton.vue.script.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSidebar.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIconButton,\n ScalarModal,\n ScalarSidebarItem,\n useModal,\n type WorkspaceGroup,\n} from '@scalar/components'\nimport { isMacOS } from '@scalar/helpers/general/is-mac-os'\nimport {\n ScalarIconDotsThree,\n ScalarIconGearSix,\n ScalarIconPlus,\n} from '@scalar/icons'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { DraggingItem, HoveredItem, SidebarState } from '@scalar/sidebar'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { getParentEntry } from '@scalar/workspace-store/navigation'\nimport type {\n TraversedEntry,\n TraversedOperation,\n} from '@scalar/workspace-store/schemas/navigation'\nimport { capitalize, computed, nextTick, ref } from 'vue'\n\nimport Rabbit from '@/assets/rabbit.ascii?raw'\nimport RabbitJump from '@/assets/rabbitjump.ascii?raw'\nimport ScalarAsciiArt from '@/components/ScalarAsciiArt.vue'\nimport DeleteSidebarListElement from '@/components/Sidebar/Actions/DeleteSidebarListElement.vue'\nimport { Sidebar } from '@/v2/components/sidebar'\nimport DownloadAppButton from '@/v2/features/app/components/DownloadAppButton.vue'\nimport SidebarItemMenu from '@/v2/features/app/components/SidebarItemMenu.vue'\nimport { createTempOperation } from '@/v2/features/app/helpers/create-temp-operation'\nimport { dragHandleFactory } from '@/v2/helpers/drag-handle-factory'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nconst { sidebarState, layout, activeWorkspace, workspaces, store, eventBus } =\n defineProps<{\n /**\n * The current layout of the app (e.g., 'desktop', 'web')\n */\n layout: ClientLayout\n\n /**\n * The sidebar state, holding navigation items and state\n */\n sidebarState: SidebarState<TraversedEntry>\n\n /**\n * Whether the workspace overview sidebar is currently open\n */\n isWorkspaceOpen?: boolean\n /**\n * The currently active workspace.\n * This represents the workspace that the user is currently working in.\n */\n activeWorkspace: { id: string; label: string }\n /**\n * The list of all available workspaces.\n * Used to render options for workspace switching and selection.\n */\n workspaces: WorkspaceGroup[]\n /**\n * The workspace event bus for handling workspace-level events.\n * Used for triggering and responding to workspace changes and actions.\n */\n eventBus: WorkspaceEventBus\n /**\n * The WorkspaceStore instance for managing workspace state and actions.\n * Provides methods and state for interacting with the current workspace.\n */\n store: WorkspaceStore\n }>()\n\nconst emit = defineEmits<{\n /** Emitted when the workspace button in the sidebar is clicked */\n (e: 'click:workspace'): void\n /** Emitted when a navigation or sidebar item is selected by ID */\n (e: 'selectItem', id: string): void\n /** Emitted when a workspace is selected by optional ID */\n (e: 'select:workspace', id?: string): void\n /** Emitted when the user requests to create a new workspace */\n (e: 'create:workspace'): void\n}>()\n\ndefineSlots<{\n /** Slot for customizing the actions section of the sidebar menu. */\n sidebarMenuActions?(): unknown\n}>()\n\n/** The label for the workspace button in the sidebar */\nconst workspaceLabel = computed(() => capitalize(activeWorkspace.label))\n\n/** Controls the visibility of the sidebar */\nconst isSidebarOpen = defineModel<boolean>('isSidebarOpen', {\n required: true,\n})\n\n/** Controls the width of the sidebar */\nconst sidebarWidth = defineModel<number>('sidebarWidth', {\n required: true,\n default: 288,\n})\n\n/** Calculate if we should show the getting started section */\nconst showGettingStarted = computed(() => sidebarState.items.value.length <= 1)\n\n/*\n * Setup drag and drop handlers for sidebar items.\n * The dragHandleFactory takes the current workspace store and sidebar state,\n * and returns the appropriate handlers for drag ending and droppability.\n *\n * We use computed to ensure the handlers are recreated when the store or sidebarState changes,\n * so they always have access to the latest values.\n */\nconst dragHandlers = computed(() =>\n dragHandleFactory({\n store,\n sidebarState,\n }),\n)\n\nconst handleDragEnd = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => {\n return dragHandlers.value.handleDragEnd(draggingItem, hoveredItem)\n}\n\nconst isDroppable = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => {\n return dragHandlers.value.isDroppable(draggingItem, hoveredItem)\n}\n\n/** The current target for the dropdown menu */\nconst menuTarget = ref<{\n /** The sidebar item that the menu is targeting */\n item: TraversedEntry\n /** A reference to the element that the menu is for */\n el: HTMLElement\n /** Whether or not to show the menu */\n showMenu: boolean\n} | null>(null)\n\nconst deleteModalState = useModal()\n\n/** Computes the message for the delete modal */\nconst deleteMessage = computed(() => {\n const item = menuTarget.value?.item\n\n if (item?.type === 'document') {\n return \"This cannot be undone. You're about to delete the document and all tags and operations inside it.\"\n }\n\n return `Are you sure you want to delete this ${item?.type ?? 'item'}? This action cannot be undone.`\n})\n\n/** Deletes an item from the sidebar by emitting the appropriate event */\nconst handleDelete = () => {\n const item = menuTarget.value?.item\n\n if (!item) {\n return\n }\n\n const result = sidebarState.getEntryById(item.id)\n\n const document = getParentEntry('document', result)\n const operation = getParentEntry('operation', result)\n\n if (!document) {\n return\n }\n\n if (item.type === 'document') {\n eventBus.emit('document:delete:document', { name: document.name })\n } else if (item.type === 'tag') {\n eventBus.emit('tag:delete:tag', {\n documentName: document.name,\n name: item.name,\n })\n } else if (item.type === 'operation') {\n if (!operation) {\n return\n }\n\n eventBus.emit('operation:delete:operation', {\n meta: {\n method: operation.method,\n path: operation.path,\n },\n documentName: document.name,\n })\n } else if (item.type === 'example') {\n if (!operation) {\n return\n }\n\n eventBus.emit('operation:delete:example', {\n meta: {\n method: operation.method,\n path: operation.path,\n exampleKey: item.name,\n },\n documentName: document.name,\n })\n }\n\n /** Clean up after deletion */\n deleteModalState.hide()\n menuTarget.value = null\n}\n\n/** Opens the dropdown menu for the given item */\nconst openMenu = async (\n event: MouseEvent | KeyboardEvent,\n item: TraversedEntry,\n) => {\n if (menuTarget.value?.showMenu) {\n return\n }\n\n const el = event.currentTarget as HTMLElement\n menuTarget.value = { item, el, showMenu: true }\n\n // Wait for the target to bind to the element\n await nextTick()\n\n // Re-dispatch the event on the target to open the menu\n const cloned =\n event instanceof MouseEvent\n ? new MouseEvent(event.type, event)\n : new KeyboardEvent(event.type, event)\n\n menuTarget.value?.el.dispatchEvent(cloned)\n}\n\n/** Closes the dropdown menu */\nconst closeMenu = () => {\n if (menuTarget.value) {\n menuTarget.value.showMenu = false\n }\n}\n\n/**\n * Creates a new operation directly from the empty folder slot.\n * Uses a unique temporary path to avoid conflicts, then navigates to the operation,\n * updates the path to `/`, and focuses the address bar so the user can immediately start typing.\n */\nconst handleAddEmptyFolder = (item: TraversedEntry) => {\n const itemWithParent = sidebarState.getEntryById(item.id)\n const documentName = getParentEntry('document', itemWithParent)?.name\n const tagName = getParentEntry('tag', itemWithParent)?.name\n\n if (!documentName) {\n console.error('Document name not found')\n return\n }\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus,\n tags: tagName ? [tagName] : undefined,\n })\n}\n\n/**\n * Navigates to the operations page for the provided operation item.\n * Emits a navigation event for the operation overview page.\n */\nconst navigateToOperationsPage = (item: TraversedOperation) => {\n const operationWithParent = sidebarState.getEntryById(item.id)\n const documentSlug = getParentEntry('document', operationWithParent)?.name\n\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'overview',\n operationPath: item.path,\n method: item.method,\n documentSlug: documentSlug,\n })\n}\n</script>\n\n<template>\n <div class=\"flex\">\n <Sidebar\n v-model:sidebarWidth=\"sidebarWidth\"\n :activeWorkspace=\"activeWorkspace\"\n :class=\"[\n 'max-md:inset-y-0 max-md:z-2 max-md:w-full!',\n isSidebarOpen ? 'max-md:absolute! max-md:flex!' : 'max-md:hidden!',\n ]\"\n :documents=\"Object.values(store.workspace.documents)\"\n :isDroppable=\"isDroppable\"\n :layout=\"layout\"\n :sidebarState=\"sidebarState\"\n :workspaces=\"workspaces\"\n @create:workspace=\"emit('create:workspace')\"\n @navigate:to:settings=\"\n eventBus.emit('ui:navigate', { page: 'workspace', path: 'settings' })\n \"\n @reorder=\"\n (draggingItem, hoveredItem) => handleDragEnd(draggingItem, hoveredItem)\n \"\n @select:workspace=\"(id) => emit('select:workspace', id)\"\n @selectItem=\"(id) => emit('selectItem', id)\">\n <template #sidebarMenuActions>\n <slot name=\"sidebarMenuActions\" />\n </template>\n <!-- Workspace Identifier -->\n <template #workspaceButton>\n <ScalarSidebarItem\n is=\"button\"\n :active=\"isWorkspaceOpen\"\n @click=\"emit('click:workspace')\">\n {{ workspaceLabel }}\n </ScalarSidebarItem>\n </template>\n\n <!-- Decorator dropdown menu -->\n <template #decorator=\"{ item }\">\n <div class=\"flex items-center gap-0.5\">\n <ScalarIconButton\n v-if=\"item.type === 'operation'\"\n :icon=\"ScalarIconGearSix\"\n label=\"Operation settings\"\n size=\"sm\"\n weight=\"bold\"\n @click.stop=\"navigateToOperationsPage(item)\"\n @keydown.enter.stop=\"navigateToOperationsPage(item)\"\n @keydown.space.stop=\"navigateToOperationsPage(item)\" />\n <ScalarIconButton\n aria-expanded=\"false\"\n aria-haspopup=\"menu\"\n :icon=\"ScalarIconDotsThree\"\n label=\"More options\"\n size=\"sm\"\n weight=\"bold\"\n @click.stop=\"(e: MouseEvent) => openMenu(e, item)\"\n @keydown.down.stop=\"(e: KeyboardEvent) => openMenu(e, item)\"\n @keydown.enter.stop=\"(e: KeyboardEvent) => openMenu(e, item)\"\n @keydown.space.stop=\"(e: KeyboardEvent) => openMenu(e, item)\"\n @keydown.up.stop=\"(e: KeyboardEvent) => openMenu(e, item)\" />\n </div>\n </template>\n\n <!-- Dirty document icon slot -->\n <template #icon=\"{ item }\">\n <template\n v-if=\"\n item.type === 'document' &&\n store.workspace.documents[item.name]?.['x-scalar-is-dirty'] === true\n \">\n <div class=\"relative flex items-center\">\n <LibraryIcon\n class=\"block\"\n :src=\"\n ('icon' in item && item.icon) || 'interface-content-folder'\n \" />\n <div\n class=\"bg-c-accent absolute -top-0.5 -right-0.5 size-1.5 rounded-full\">\n <span class=\"sr-only\">Unsaved changes</span>\n </div>\n </div>\n </template>\n </template>\n\n <!-- Empty folder slot -->\n <template #empty=\"{ item }\">\n <ScalarSidebarItem\n is=\"button\"\n @click=\"handleAddEmptyFolder(item)\">\n <template #icon>\n <ScalarIconPlus />\n </template>\n <template #default>Add operation</template>\n </ScalarSidebarItem>\n </template>\n\n <!-- Getting started section -->\n <template\n v-if=\"layout !== 'modal'\"\n #footer>\n <div\n :class=\"{\n 'empty-sidebar-item border-t': showGettingStarted,\n }\">\n <div\n v-if=\"showGettingStarted\"\n class=\"empty-sidebar-item-content overflow-hidden px-2.5 py-2.5\">\n <div class=\"rabbit-ascii relative m-auto mt-2 h-[68px] w-[60px]\">\n <ScalarAsciiArt\n :art=\"Rabbit\"\n class=\"rabbitsit font-bold\" />\n <ScalarAsciiArt\n :art=\"RabbitJump\"\n class=\"rabbitjump absolute top-0 left-0 font-bold\" />\n </div>\n <div class=\"mt-2 mb-2 text-center text-sm text-balance\">\n <b class=\"font-medium\">Let's Get Started</b>\n <p class=\"mt-2 leading-5\">\n Create request, folder, collection or import from\n OpenAPI/Postman\n </p>\n </div>\n </div>\n\n <div class=\"flex flex-col gap-1.5 p-2\">\n <ScalarButton\n v-if=\"showGettingStarted\"\n class=\"w-full\"\n size=\"sm\"\n @click=\"\n eventBus.emit('ui:open:command-palette', {\n action: 'import-from-openapi-swagger-postman-curl',\n payload: undefined,\n })\n \">\n Import Collection\n </ScalarButton>\n\n <ScalarButton\n class=\"w-full\"\n hotkey=\"K\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"eventBus.emit('ui:open:command-palette')\">\n Add Item \n\n <span\n class=\"text-sidebar-c-2 rounded border px-1.25 py-1 text-xs leading-none font-medium uppercase\">\n <template v-if=\"isMacOS()\">\n <span class=\"sr-only\">Command</span>\n <span aria-hidden=\"true\">⌘</span>\n </template>\n <template v-else>\n <span class=\"sr-only\">CTRL</span>\n <span aria-hidden=\"true\">⌃</span>\n </template>\n K\n </span>\n </ScalarButton>\n <DownloadAppButton v-if=\"layout === 'web'\" />\n </div>\n </div>\n </template>\n </Sidebar>\n <SidebarItemMenu\n v-if=\"menuTarget?.showMenu\"\n :eventBus=\"eventBus\"\n :item=\"menuTarget.item\"\n :sidebarState=\"sidebarState\"\n :target=\"menuTarget.el\"\n :workspaceStore=\"store\"\n @closeMenu=\"closeMenu\"\n @showDeleteModal=\"deleteModalState.show()\" />\n\n <!-- Delete Modal -->\n <ScalarModal\n v-if=\"menuTarget\"\n size=\"xxs\"\n :state=\"deleteModalState\"\n :title=\"`Delete ${menuTarget.item.title}`\">\n <DeleteSidebarListElement\n :variableName=\"menuTarget.item.title\"\n :warningMessage=\"deleteMessage\"\n @close=\"deleteModalState.hide()\"\n @delete=\"handleDelete\" />\n </ScalarModal>\n </div>\n</template>\n\n<style scoped>\n.empty-sidebar-item-content {\n display: none;\n}\n.empty-sidebar-item .empty-sidebar-item-content {\n display: block;\n}\n.rabbitjump {\n opacity: 0;\n}\n.empty-sidebar-item:hover .rabbitjump {\n opacity: 1;\n animation: rabbitAnimation 0.5s steps(1) infinite;\n}\n.empty-sidebar-item:hover .rabbitsit {\n opacity: 0;\n animation: rabbitAnimation2 0.5s steps(1) infinite;\n}\n.empty-sidebar-item:hover .rabbit-ascii {\n animation: rabbitRun 8s infinite linear;\n}\n@keyframes rabbitRun {\n 0% {\n transform: translate3d(0, 0, 0);\n }\n 25% {\n transform: translate3d(250px, 0, 0);\n }\n 25.01% {\n transform: translate3d(-250px, 0, 0);\n }\n 75% {\n transform: translate3d(250px, 0, 0);\n }\n 75.01% {\n transform: translate3d(-250px, 0, 0);\n }\n 100% {\n transform: translate3d(0, 0, 0);\n }\n}\n@keyframes rabbitAnimation {\n 0%,\n 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n}\n@keyframes rabbitAnimation2 {\n 0%,\n 100% {\n opacity: 0;\n }\n 50% {\n opacity: 1;\n transform: translate3d(0, -8px, 0);\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2EA,MAAM,OAAO;;EAiBb,MAAM,iBAAiB,eAAe,WAAW,QAAA,gBAAgB,MAAM,CAAA;;EAGvE,MAAM,gBAAgB,SAAoB,SAAC,gBAE1C;;EAGD,MAAM,eAAe,SAAmB,SAAC,eAGxC;;EAGD,MAAM,qBAAqB,eAAe,QAAA,aAAa,MAAM,MAAM,UAAU,EAAC;EAU9E,MAAM,eAAe,eACnB,kBAAkB;GAChB,OAAI,QAAA;GACJ,cAAW,QAAA;GACZ,CAAC,CACJ;EAEA,MAAM,iBACJ,cACA,gBACY;AACZ,UAAO,aAAa,MAAM,cAAc,cAAc,YAAW;;EAGnE,MAAM,eACJ,cACA,gBACY;AACZ,UAAO,aAAa,MAAM,YAAY,cAAc,YAAW;;;EAIjE,MAAM,aAAa,IAOT,KAAI;EAEd,MAAM,mBAAmB,UAAS;;EAGlC,MAAM,gBAAgB,eAAe;GACnC,MAAM,OAAO,WAAW,OAAO;AAE/B,OAAI,MAAM,SAAS,WACjB,QAAO;AAGT,UAAO,wCAAwC,MAAM,QAAQ,OAAO;IACrE;;EAGD,MAAM,qBAAqB;GACzB,MAAM,OAAO,WAAW,OAAO;AAE/B,OAAI,CAAC,KACH;GAGF,MAAM,SAAS,QAAA,aAAa,aAAa,KAAK,GAAE;GAEhD,MAAM,WAAW,eAAe,YAAY,OAAM;GAClD,MAAM,YAAY,eAAe,aAAa,OAAM;AAEpD,OAAI,CAAC,SACH;AAGF,OAAI,KAAK,SAAS,WAChB,SAAA,SAAS,KAAK,4BAA4B,EAAE,MAAM,SAAS,MAAM,CAAA;YACxD,KAAK,SAAS,MACvB,SAAA,SAAS,KAAK,kBAAkB;IAC9B,cAAc,SAAS;IACvB,MAAM,KAAK;IACZ,CAAA;YACQ,KAAK,SAAS,aAAa;AACpC,QAAI,CAAC,UACH;AAGF,YAAA,SAAS,KAAK,8BAA8B;KAC1C,MAAM;MACJ,QAAQ,UAAU;MAClB,MAAM,UAAU;MACjB;KACD,cAAc,SAAS;KACxB,CAAA;cACQ,KAAK,SAAS,WAAW;AAClC,QAAI,CAAC,UACH;AAGF,YAAA,SAAS,KAAK,4BAA4B;KACxC,MAAM;MACJ,QAAQ,UAAU;MAClB,MAAM,UAAU;MAChB,YAAY,KAAK;MAClB;KACD,cAAc,SAAS;KACxB,CAAA;;;AAIH,oBAAiB,MAAK;AACtB,cAAW,QAAQ;;;EAIrB,MAAM,WAAW,OACf,OACA,SACG;AACH,OAAI,WAAW,OAAO,SACpB;AAIF,cAAW,QAAQ;IAAE;IAAM,IADhB,MAAM;IACc,UAAU;IAAK;AAG9C,SAAM,UAAS;GAGf,MAAM,SACJ,iBAAiB,aACb,IAAI,WAAW,MAAM,MAAM,MAAK,GAChC,IAAI,cAAc,MAAM,MAAM,MAAK;AAEzC,cAAW,OAAO,GAAG,cAAc,OAAM;;;EAI3C,MAAM,kBAAkB;AACtB,OAAI,WAAW,MACb,YAAW,MAAM,WAAW;;;;;;;EAShC,MAAM,wBAAwB,SAAyB;GACrD,MAAM,iBAAiB,QAAA,aAAa,aAAa,KAAK,GAAE;GACxD,MAAM,eAAe,eAAe,YAAY,eAAe,EAAE;GACjE,MAAM,UAAU,eAAe,OAAO,eAAe,EAAE;AAEvD,OAAI,CAAC,cAAc;AACjB,YAAQ,MAAM,0BAAyB;AACvC;;AAEF,uBAAoB,cAAc;IAChC,eAAe,IAAI,IACjB,OAAO,KAAK,QAAA,MAAM,UAAU,UAAU,eAAe,SAAS,EAAE,CAAC,CAClE;IACD,UAAO,QAAA;IACP,MAAM,UAAU,CAAC,QAAQ,GAAG,KAAA;IAC7B,CAAA;;;;;;EAOH,MAAM,4BAA4B,SAA6B;GAE7D,MAAM,eAAe,eAAe,YADR,QAAA,aAAa,aAAa,KAAK,GAAE,CACO,EAAE;AAEtE,WAAA,SAAS,KAAK,eAAe;IAC3B,MAAM;IACN,MAAM;IACN,eAAe,KAAK;IACpB,QAAQ,KAAK;IACC;IACf,CAAA;;;uBAKD,mBAyLM,OAzLN,YAyLM;IAxLJ,YAiKU,MAAA,gBAAA,EAAA;KAhKA,cAAc,aAAA;iFAAY,QAAA;KACjC,iBAAiB,QAAA;KACjB,OAAK,eAAA,CAAA,8CAAkE,cAAA,QAAa,kCAAA,iBAAA,CAAA;KAIpF,WAAW,OAAO,OAAO,QAAA,MAAM,UAAU,UAAS;KACrC;KACb,QAAQ,QAAA;KACR,cAAc,QAAA;KACd,YAAY,QAAA;KACZ,sBAAgB,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,mBAAA;KACtB,0BAAoB,OAAA,OAAA,OAAA,MAAA,WAAW,QAAA,SAAS,KAAI,eAAA;MAAA,MAAA;MAAA,MAAA;MAAA,CAAA;KAG5C,WAAO,OAAA,OAAA,OAAA,MAAY,cAAc,gBAAgB,cAAc,cAAc,YAAW;KAGxF,sBAAgB,OAAA,OAAA,OAAA,MAAG,OAAO,KAAI,oBAAqB,GAAE;KACrD,cAAU,OAAA,OAAA,OAAA,MAAG,OAAO,KAAI,cAAe,GAAE;;KAC/B,oBAAkB,cACO,CAAlC,WAAkC,KAAA,QAAA,sBAAA,EAAA,EAAA,KAAA,GAAA,KAAA,CAAA,CAAA;KAGzB,iBAAe,cAMJ,CALpB,YAKoB,MAAA,kBAAA,EAAA;MAJlB,IAAG;MACF,QAAQ,QAAA;MACR,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,kBAAA;;6BACQ,CAAA,gBAAA,gBAAjB,eAAA,MAAc,EAAA,EAAA,CAAA,CAAA;;;KAKV,WAAS,SAuBZ,EAvBgB,WAAI,CAC1B,mBAsBM,OAtBN,YAsBM,CApBI,KAAK,SAAI,eAAA,WAAA,EADjB,YAQyD,MAAA,iBAAA,EAAA;;MANtD,MAAM,MAAA,kBAAiB;MACxB,OAAM;MACN,MAAK;MACL,QAAO;MACN,SAAK,eAAA,WAAO,yBAAyB,KAAI,EAAA,CAAA,OAAA,CAAA;MACzC,WAAO,CAAA,SAAA,eAAA,WAAa,yBAAyB,KAAI,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,QAAA,CAAA,EAAA,SAAA,eAAA,WAC7B,yBAAyB,KAAI,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,QAAA,CAAA,CAAA;;;;;yCACpD,YAW+D,MAAA,iBAAA,EAAA;MAV7D,iBAAc;MACd,iBAAc;MACb,MAAM,MAAA,oBAAmB;MAC1B,OAAM;MACN,MAAK;MACL,QAAO;MACN,SAAK,eAAQ,MAAkB,SAAS,GAAG,KAAI,EAAA,CAAA,OAAA,CAAA;MAC/C,WAAO;+BAAa,MAAqB,SAAS,GAAG,KAAI,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,OAAA,CAAA;+BACpC,MAAqB,SAAS,GAAG,KAAI,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,QAAA,CAAA;+BACrC,MAAqB,SAAS,GAAG,KAAI,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,QAAA,CAAA;+BACxC,MAAqB,SAAS,GAAG,KAAI,EAAA,CAAA,OAAA,CAAA,EAAA,CAAA,KAAA,CAAA;;;;;;;KAKnD,MAAI,SAiBF,EAjBM,WAAI,CAEA,KAAK,SAAI,cAA+B,QAAA,MAAM,UAAU,UAAU,KAAK,QAAI,yBAAA,QAAA,WAAA,EAI9F,mBAUM,OAVN,YAUM,CATJ,YAIM,MAAA,YAAA,EAAA;MAHJ,OAAM;MACL,KAAA,UAAiC,QAAQ,KAAK,QAAI;uDAGrD,mBAGM,OAAA,EAFJ,OAAM,kEAAgE,EAAA,CACtE,mBAA4C,QAAA,EAAtC,OAAM,WAAS,EAAC,kBAAe,CAAA,EAAA,GAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;KAOlC,OAAK,SAQM,EARF,WAAI,CACtB,YAOoB,MAAA,kBAAA,EAAA;MANlB,IAAG;MACF,UAAK,WAAE,qBAAqB,KAAI;;MACtB,MAAI,cACK,CAAlB,YAAkB,MAAA,eAAA,CAAA,CAAA,CAAA;MAET,SAAO,cAAc,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAAb,iBAAa,GAAA,CAAA,EAAA,CAAA;;;;QAM5B,QAAA,WAAM,UAAA;WACX;uBA8DK,CA7DN,mBA6DM,OAAA,EA5DH,OAAK,eAAA,EAAA,+BAA+C,mBAAA,OAAA,CAAA,EAAA,EAAA,CAI7C,mBAAA,SAAA,WAAA,EADR,mBAkBM,OAlBN,YAkBM,CAfJ,mBAOM,OAPN,YAOM,CANJ,YAEgC,wBAAA;MAD7B,KAAK,MAAA,eAAM;MACZ,OAAM;2BACR,YAEuD,wBAAA;MADpD,KAAK,MAAA,mBAAU;MAChB,OAAM;yDAEV,mBAMM,OAAA,EAND,OAAM,8CAA4C,EAAA,CACrD,mBAA4C,KAAA,EAAzC,OAAM,eAAa,EAAC,oBAAiB,EACxC,mBAGI,KAAA,EAHD,OAAM,kBAAgB,EAAC,sEAG1B,CAAA,EAAA,GAAA,EAAA,CAAA,IAAA,mBAAA,IAAA,KAAA,EAIJ,mBAoCM,OApCN,YAoCM;MAlCI,mBAAA,SAAA,WAAA,EADR,YAWe,MAAA,aAAA,EAAA;;OATb,OAAM;OACN,MAAK;OACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAmB,QAAA,SAAS,KAAI,2BAAA;;iBAA+H,KAAA;;;8BAOvK,CAAA,GAAA,OAAA,QAAA,OAAA,MAAA,CAAA,gBAFI,uBAEJ,GAAA,CAAA,EAAA,CAAA;;;MAEA,YAoBe,MAAA,aAAA,EAAA;OAnBb,OAAM;OACN,QAAO;OACP,MAAK;OACL,SAAQ;OACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,QAAA,SAAS,KAAI,0BAAA;;8BAGrB,CAAA,OAAA,QAAA,OAAA,MAAA,gBAHkD,mBAGlD,GAAA,GAAA,mBAWO,QAXP,YAWO,CATW,MAAA,QAAO,EAAA,IAAA,WAAA,EAAvB,mBAGW,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,OAAA,QAAA,OAAA,MAFT,mBAAoC,QAAA,EAA9B,OAAM,WAAS,EAAC,WAAO,GAAA,GAAA,OAAA,QAAA,OAAA,MAC7B,mBAAiC,QAAA,EAA3B,eAAY,QAAM,EAAC,KAAC,GAAA,EAAA,EAAA,GAAA,KAAA,WAAA,EAE5B,mBAGW,UAAA,EAAA,KAAA,GAAA,EAAA,CAAA,OAAA,QAAA,OAAA,MAFT,mBAAiC,QAAA,EAA3B,OAAM,WAAS,EAAC,QAAI,GAAA,GAAA,OAAA,QAAA,OAAA,MAC1B,mBAAiC,QAAA,EAA3B,eAAY,QAAM,EAAC,KAAC,GAAA,EAAA,EAAA,GAAA,GAAA,OAAA,QAAA,OAAA,MAAA,gBACjB,OAEb,GAAA,EAAA,CAAA,CAAA,CAAA;;;MAEuB,QAAA,WAAM,SAAA,WAAA,EAA/B,YAA6C,2BAAA,EAAA,KAAA,GAAA,CAAA,IAAA,mBAAA,IAAA,KAAA;;;;;;;;;;;;IAM7C,WAAA,OAAY,YAAA,WAAA,EADpB,YAQ+C,yBAAA;;KAN5C,UAAU,QAAA;KACV,MAAM,WAAA,MAAW;KACjB,cAAc,QAAA;KACd,QAAQ,WAAA,MAAW;KACnB,gBAAgB,QAAA;KAChB,aAAW;KACX,mBAAe,OAAA,OAAA,OAAA,MAAA,WAAE,MAAA,iBAAgB,CAAC,MAAI;;;;;;;;IAIjC,WAAA,SAAA,WAAA,EADR,YAUc,MAAA,YAAA,EAAA;;KARZ,MAAK;KACJ,OAAO,MAAA,iBAAgB;KACvB,OAAK,UAAY,WAAA,MAAW,KAAK;;4BAKP,CAJ3B,YAI2B,kCAAA;MAHxB,cAAc,WAAA,MAAW,KAAK;MAC9B,gBAAgB,cAAA;MAChB,SAAK,OAAA,QAAA,OAAA,OAAA,WAAE,MAAA,iBAAgB,CAAC,MAAI;MAC5B,UAAQ"}
|
|
1
|
+
{"version":3,"file":"AppSidebar.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { AppState } from '@scalar/api-client/v2/features/app'\nimport {\n ScalarIconButton,\n ScalarModal,\n ScalarSidebar,\n ScalarSidebarButton,\n ScalarSidebarItem,\n ScalarSidebarItems,\n ScalarSidebarNestedItems,\n ScalarSidebarSearchInput,\n ScalarSidebarSection,\n useModal,\n} from '@scalar/components'\nimport {\n ScalarIconCaretLeft,\n ScalarIconDotsThree,\n ScalarIconFolderDashed,\n ScalarIconFunnel,\n ScalarIconGearSix,\n ScalarIconMagnifyingGlass,\n ScalarIconPlus,\n} from '@scalar/icons'\nimport {\n filterItems,\n SidebarItem,\n type DraggingItem,\n type HoveredItem,\n} from '@scalar/sidebar'\nimport { useToasts } from '@scalar/use-toasts'\nimport { getParentEntry } from '@scalar/workspace-store/navigation'\nimport type { TraversedEntry } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'\n\nimport DeleteSidebarListElement from '@/components/Sidebar/Actions/DeleteSidebarListElement.vue'\nimport { Resize } from '@/v2/components/resize'\nimport SidebarItemMenu from '@/v2/features/app/components/SidebarItemMenu.vue'\nimport { createTempOperation } from '@/v2/features/app/helpers/create-temp-operation'\nimport { loadRegistryDocument } from '@/v2/features/app/helpers/load-registry-document'\nimport { useDocumentFilter } from '@/v2/features/app/hooks/use-document-filter'\nimport { useSidebarContextMenu } from '@/v2/features/app/hooks/use-sidebar-context-menu'\nimport {\n useSidebarDocuments,\n type RegistryDocumentsState,\n type SidebarDocumentItem,\n} from '@/v2/features/app/hooks/use-sidebar-documents'\nimport { DocumentSearchModal } from '@/v2/features/search'\nimport { dragHandleFactory } from '@/v2/helpers/drag-handle-factory'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nconst {\n app,\n indent = 20,\n registryDocuments = { status: 'success', documents: [] },\n fetchRegistryDocument,\n} = defineProps<{\n /** The app state from @scalar/api-client. */\n app: AppState\n /** Horizontal indent applied to nested sidebar items, in pixels. */\n indent?: number\n /**\n * The list of all available registry documents, wrapped in a loading state\n * so the sidebar can render skeleton placeholders while the registry is\n * still being fetched.\n */\n registryDocuments?: RegistryDocumentsState\n /** A function to fetch a registry document */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n}>()\n\nconst { toast } = useToasts()\n\n/**\n * Whether the caller is still fetching the list of registry documents. We\n * only surface the loading state on team workspaces because local workspaces\n * never consult the registry\n */\nconst isLoadingRegistry = computed(\n () =>\n registryDocuments.status === 'loading' &&\n app.workspace.isTeamWorkspace.value,\n)\n\nconst { rest } = useSidebarDocuments({\n app,\n managedDocs: () => registryDocuments.documents ?? [],\n})\n\n/**\n * Whether the workspace truly has no documents to show. Distinct from the\n * filter producing no results: we only surface the \"No APIs yet\" empty state\n * when the workspace is genuinely empty so users see a clear call-to-action\n * instead of a confusing blank space.\n */\nconst isEmpty = computed(\n () => !isLoadingRegistry.value && rest.value.length === 0,\n)\n\n/**\n * Fuzzy filter over the top-level documents. Owns its own input visibility,\n * query string and Fuse index. See `use-document-filter.ts` for details.\n */\nconst {\n isVisible: isFilterVisible,\n query: filterQuery,\n filteredItems: filteredRest,\n toggle: toggleFilter,\n} = useDocumentFilter(rest)\n\nconst sidebarState = app.sidebar.state\n\n/** Which registry documents are currently being fetched. */\nconst loadingKeys = ref<Record<string, boolean>>({})\n\n/**\n * Check if the given {@link SidebarDocumentItem} is the currently active document (from the sidebar state).\n */\nconst isDocActive = (item: SidebarDocumentItem) => {\n if (!item.navigation) {\n return false\n }\n\n return (\n sidebarState.selectedItem.value === item.navigation.id ||\n Boolean(sidebarState.selectedItems.value[item.navigation.id])\n )\n}\n\nconst handleDocumentClick = async (item: SidebarDocumentItem) => {\n if (item.navigation) {\n app.sidebar.handleSelectItem(item.navigation.id)\n return\n }\n\n if (!item.registry || !app.store.value) {\n console.warn('Document does not have a sidebar navigation, skipping...')\n return\n }\n\n if (!fetchRegistryDocument) {\n console.warn(\n 'You need to provide a fetchRegistryDocument function to load registry documents',\n )\n return\n }\n\n if (loadingKeys.value[item.key]) {\n return\n }\n\n loadingKeys.value[item.key] = true\n\n const result = await loadRegistryDocument({\n fetcher: fetchRegistryDocument,\n workspaceStore: app.store.value,\n namespace: item.registry.namespace,\n slug: item.registry.slug,\n })\n\n loadingKeys.value[item.key] = false\n\n if (!result.ok) {\n toast(result.error, 'error')\n return\n }\n\n // After loading, route to the document overview. `syncSidebar` will then\n // mark the document as selected and the template's `:open=\"isDocActive\"`\n // binding drills the sidebar in automatically — no local state needed.\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'overview',\n documentSlug: result.documentName,\n })\n}\n\nconst isSelected = (id: string) => sidebarState.isSelected(id)\nconst isExpanded = (id: string) => sidebarState.isExpanded(id)\n\nconst handleSelectItem = (id: string) => {\n app.sidebar.handleSelectItem(id)\n}\n\nconst handleToggleGroup = (id: string) => {\n sidebarState.setExpanded(id, !sidebarState.isExpanded(id))\n}\n\n/**\n * Drag-and-drop handlers for sidebar items. The factory reads from the live\n * workspace store and shared sidebar state, so it reflects the latest\n * navigation tree on every drag. Mirrors the behaviour of the old\n * `AppSidebar.vue` (documents, tags, and operations can be reordered, and\n * operations can be moved between tags/documents).\n */\nconst dragHandlers = computed(() =>\n dragHandleFactory({\n store: app.store,\n sidebarState,\n }),\n)\n\nconst handleDragEnd = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => dragHandlers.value.handleDragEnd(draggingItem, hoveredItem)\n\nconst isDroppable = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => dragHandlers.value.isDroppable(draggingItem, hoveredItem)\n\n/**\n * Contextual \"more\" dropdown for tags, operations and examples, together\n * with the shared delete-confirmation modal it triggers.\n */\nconst {\n menuTarget,\n deleteModalState,\n deleteMessage,\n openMenu,\n closeMenu,\n handleDelete,\n} = useSidebarContextMenu({\n eventBus: app.eventBus,\n sidebarState,\n})\n\n/**\n * Create a new operation from an empty folder slot inside a tag or document.\n * If the entry is a tag, the new operation inherits that tag so it stays\n * grouped under the same folder.\n */\nconst handleAddEmptyFolder = (item: TraversedEntry) => {\n const itemWithParent = sidebarState.getEntryById(item.id)\n const documentName = getParentEntry('document', itemWithParent)?.name\n const tagName = getParentEntry('tag', itemWithParent)?.name\n const store = app.store.value\n\n if (!documentName || !store) {\n console.error('Document name not found')\n return\n }\n\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus: app.eventBus,\n tags: tagName ? [tagName] : undefined,\n })\n}\n\nconst handleCreate = () => {\n app.eventBus.emit('ui:open:command-palette', {\n action: 'create-openapi-document',\n payload: undefined,\n })\n}\n\n/**\n * Create a new operation inside the given document and immediately navigate to\n * it. Uses `createTempOperation` so the operation gets a unique `/temp…` path,\n * then the sidebar focuses the new example and the address bar is focused so\n * the user can start typing the real path right away.\n */\nconst handleCreateOperation = (item: SidebarDocumentItem) => {\n const documentName = item.documentName\n const store = app.store.value\n\n if (!documentName || !store) {\n console.warn('Cannot create an operation: no document loaded')\n return\n }\n\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus: app.eventBus,\n })\n}\n\n/**\n * Navigates back to the workspace \"Get started\" page.\n */\nconst handleBack = () => {\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'get-started',\n })\n}\n\n/**\n * True when the user is currently viewing a document (any of its subpages).\n * When inside a document, the sidebar actions are scoped to that document:\n * the gear icon opens the document (collection) settings and the filter icon\n * becomes a search icon that focuses the search input.\n */\nconst isOnDocumentPage = computed(() =>\n Boolean(app.activeEntities.documentSlug.value),\n)\n\nconst handleOpenSettings = () => {\n if (isOnDocumentPage.value) {\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'settings',\n documentSlug: app.activeEntities.documentSlug.value,\n })\n return\n }\n\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'settings',\n })\n}\n\n/**\n * Controls the per-document search modal. Only used when the user is drilled\n * into a single document and clicks the magnifying-glass icon.\n */\nconst searchModal = useModal()\n\n/**\n * The OpenAPI document currently selected in the workspace. The search modal\n * scopes its Fuse index to this document so results never leak across\n * collections.\n */\nconst activeDocument = computed(() => app.store.value?.workspace.activeDocument)\n\nconst handleFilterOrSearch = () => {\n // Inside a document, this icon opens a modal search that is scoped to that\n // single document (similar to the reference search modal), so users can jump\n // to any operation / tag / heading without noise from other documents.\n if (isOnDocumentPage.value) {\n searchModal.show()\n return\n }\n\n // At the top-level documents view, the icon toggles a lightweight filter\n // that narrows the visible documents by title.\n toggleFilter()\n}\n\n/**\n * Handle the `ui:focus:search` event. Dispatch is driven by the shared\n * `handleHotkeys` helper (Cmd/Ctrl+J) or by programmatic callers such as the\n * workspace \"Get started\" page. When a `KeyboardEvent` is included we\n * preventDefault to override the browser's Cmd+J (downloads panel), then\n * delegate to `handleFilterOrSearch`, which already branches on whether the\n * user is viewing a document or the workspace root.\n */\nconst handleSearchHotkey = (payload: { event: KeyboardEvent } | undefined) => {\n payload?.event.preventDefault()\n handleFilterOrSearch()\n}\n\n/**\n * Handle the `ui:open:settings` event (Cmd/Ctrl+I). Same delegation model as\n * `handleSearchHotkey`: preventDefault on the originating keyboard event (if\n * any) and hand off to `handleOpenSettings`, which routes to the document-\n * level settings page when a document is active and the workspace-level\n * settings page otherwise.\n */\nconst handleSettingsHotkey = (\n payload: { event: KeyboardEvent } | undefined,\n) => {\n payload?.event.preventDefault()\n handleOpenSettings()\n}\n\nonBeforeMount(() => {\n app.eventBus.on('ui:focus:search', handleSearchHotkey)\n app.eventBus.on('ui:open:settings', handleSettingsHotkey)\n})\nonBeforeUnmount(() => {\n app.eventBus.off('ui:focus:search', handleSearchHotkey)\n app.eventBus.off('ui:open:settings', handleSettingsHotkey)\n})\n\n/**\n * Navigate to the selected search result. `scroll-to:nav-item` is already\n * wired up through the app event bus to update the sidebar + route, matching\n * the behaviour used elsewhere in the app.\n */\nconst handleSearchSelect = (id: string) => {\n app.eventBus.emit('scroll-to:nav-item', { id })\n}\n\n/** Controls the width of the sidebar */\nconst sidebarWidth = defineModel<number>('sidebarWidth', {\n required: true,\n default: 288,\n})\n</script>\n\n<template>\n <Resize\n v-model:width=\"sidebarWidth\"\n class=\"flex flex-col max-md:inset-y-0 max-md:z-2 max-md:w-full!\"\n :class=\"{\n 'max-md:absolute! max-md:flex!': app.sidebar.isOpen.value,\n 'max-md:hidden!': !app.sidebar.isOpen.value,\n }\">\n <template #default>\n <div class=\"flex flex-1\">\n <ScalarSidebar\n class=\"flex min-h-0 flex-1 flex-col max-md:pt-12\"\n :style=\"{ '--scalar-sidebar-indent': indent + 'px' }\">\n <!-- Top-level sidebar header -->\n <div\n v-if=\"!isOnDocumentPage\"\n class=\"flex flex-col gap-1.5 p-(--scalar-sidebar-padding)\">\n <div class=\"flex items-center gap-1\">\n <ScalarSidebarButton\n is=\"div\"\n class=\"text-sidebar-c-1 font-sidebar-active flex-1\"\n disabled>\n All Documents\n </ScalarSidebarButton>\n <ScalarIconButton\n :icon=\"ScalarIconGearSix\"\n label=\"Workspace settings\"\n size=\"sm\"\n @click=\"handleOpenSettings\" />\n <ScalarIconButton\n :icon=\"ScalarIconFunnel\"\n label=\"Filter documents\"\n size=\"sm\"\n @click=\"handleFilterOrSearch\" />\n <ScalarIconButton\n class=\"rounded-full border\"\n :icon=\"ScalarIconPlus\"\n label=\"Add document\"\n size=\"sm\"\n @click=\"handleCreate\" />\n </div>\n <ScalarSidebarSearchInput\n v-if=\"isFilterVisible\"\n v-model=\"filterQuery\"\n autofocus />\n </div>\n\n <!-- Document list (top-level) -->\n <div class=\"custom-scroll flex flex-1 flex-col\">\n <!--\n Empty state: no documents in the workspace yet. Matches the\n minimal `empty folder` appearance.\n -->\n <div\n v-if=\"isEmpty && !isOnDocumentPage\"\n class=\"text-c-3 flex flex-1 flex-col items-center justify-center gap-2 p-6 text-center select-none\">\n <ScalarIconFolderDashed\n class=\"size-10\"\n weight=\"light\" />\n <p class=\"text-sm font-medium\">No APIs yet</p>\n </div>\n <ScalarSidebarItems v-else>\n <!-- Show pinned documents after we add support for it -->\n <!-- <ScalarSidebarSection v-if=\"pinned.length\">\n Pinned\n <template #items>\n <ScalarSidebarNestedItems\n v-for=\"item in pinned\"\n :key=\"item.key\"\n :active=\"isDocActive(item)\"\n controlled\n :open=\"Boolean(openKeys[item.key])\"\n @back=\"openKeys[item.key] = false\"\n @click=\"handleDocumentClick(item)\">\n <span class=\"truncate\">{{ item.title }}</span>\n <template #back-label>{{ item.title }}</template>\n <template #items>\n <SidebarItem\n v-for=\"child in filterItems(item.navigation?.children ?? [])\"\n :key=\"child.id\"\n :isDraggable=\"false\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"child\"\n layout=\"client\"\n @selectItem=\"handleSelectItem\"\n @toggleGroup=\"handleToggleGroup\" />\n </template>\n </ScalarSidebarNestedItems>\n </template>\n </ScalarSidebarSection> -->\n\n <ScalarSidebarSection>\n All documents\n <template #items>\n <!--\n Skeleton rows shown while the caller is still fetching\n the registry document list. We only render skeletons in\n the top-level view (when no document is drilled-in) so\n the collection view is never masked by placeholders.\n -->\n <template v-if=\"isLoadingRegistry && !isOnDocumentPage\">\n <li\n v-for=\"n in 4\"\n :key=\"`registry-skeleton-${n}`\"\n aria-hidden=\"true\"\n class=\"sidebar-skeleton-row px-(--scalar-sidebar-padding) py-1\">\n <span class=\"bg-b-3 block h-6 rounded-md\" />\n </li>\n </template>\n <ScalarSidebarNestedItems\n v-for=\"item in filteredRest\"\n :key=\"item.key\"\n :active=\"isDocActive(item)\"\n controlled\n :open=\"isDocActive(item)\"\n @back=\"handleBack\"\n @click=\"handleDocumentClick(item)\">\n <span>{{ item.title }}</span>\n <template\n v-if=\"loadingKeys[item.key]\"\n #aside>\n <span class=\"text-c-3 text-xs\">Loading…</span>\n </template>\n <!-- Document back row -->\n <template #back>\n <div class=\"flex items-center gap-1\">\n <ScalarSidebarButton\n is=\"button\"\n class=\"text-sidebar-c-1 font-sidebar-active hover:text-sidebar-c-1 flex-1\"\n @click=\"handleBack\">\n <template #icon>\n <ScalarIconCaretLeft\n class=\"text-sidebar-c-2 -m-px size-4\" />\n </template>\n Back\n </ScalarSidebarButton>\n <ScalarIconButton\n :icon=\"ScalarIconGearSix\"\n label=\"Collection settings\"\n size=\"sm\"\n @click=\"handleOpenSettings\" />\n <ScalarIconButton\n :icon=\"ScalarIconMagnifyingGlass\"\n label=\"Search collection\"\n size=\"sm\"\n @click=\"handleFilterOrSearch\" />\n <ScalarIconButton\n class=\"rounded-full border\"\n :icon=\"ScalarIconPlus\"\n label=\"Add operation\"\n size=\"sm\"\n @click=\"handleCreateOperation(item)\" />\n </div>\n </template>\n <!-- Document items (operations, tags, examples) -->\n <template #items>\n <template v-if=\"item.navigation?.children?.length\">\n <SidebarItem\n v-for=\"child in filterItems(\n 'client',\n item.navigation.children,\n )\"\n :key=\"child.id\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"child\"\n layout=\"client\"\n @onDragEnd=\"handleDragEnd\"\n @selectItem=\"handleSelectItem\"\n @toggleGroup=\"handleToggleGroup\">\n <!--\n Per-item \"more\" dropdown for tags, operations and\n examples (add / edit / delete…). The dropdown is\n rendered once at the sidebar root and anchors\n itself to whichever icon button opened it.\n Operation settings live on the operation header\n (next to the environment selector), not here.\n -->\n <template #decorator=\"{ item: entry }\">\n <ScalarIconButton\n aria-expanded=\"false\"\n aria-haspopup=\"menu\"\n class=\"bg-b-2\"\n :icon=\"ScalarIconDotsThree\"\n label=\"More options\"\n size=\"sm\"\n variant=\"ghost\"\n weight=\"bold\"\n @click.stop=\"\n (e: MouseEvent) => openMenu(e, entry)\n \"\n @keydown.down.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \"\n @keydown.enter.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \"\n @keydown.space.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \"\n @keydown.up.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \" />\n </template>\n <!--\n Empty tag / folder slot. Matches the \"Add operation\"\n affordance from the old sidebar so users can create\n an operation directly inside the hovered tag.\n -->\n <template #empty=\"{ item: emptyItem }\">\n <ScalarSidebarItem\n is=\"button\"\n @click=\"handleAddEmptyFolder(emptyItem)\">\n <template #icon>\n <ScalarIconPlus />\n </template>\n <template #default>Add operation</template>\n </ScalarSidebarItem>\n </template>\n </SidebarItem>\n </template>\n <li\n v-else\n class=\"text-c-3 px-3 py-1 text-xs\">\n Empty document\n </li>\n </template>\n </ScalarSidebarNestedItems>\n </template>\n </ScalarSidebarSection>\n </ScalarSidebarItems>\n </div>\n\n <slot name=\"footer\" />\n </ScalarSidebar>\n </div>\n <DocumentSearchModal\n :document=\"activeDocument\"\n :modalState=\"searchModal\"\n @select=\"handleSearchSelect\" />\n <!--\n Contextual dropdown menu for tags, operations and examples. Rendered\n once for the whole sidebar and re-anchored to the triggering icon via\n `menuTarget.el`, so we do not create a dropdown per item.\n -->\n <SidebarItemMenu\n v-if=\"app.store.value && menuTarget?.showMenu\"\n :eventBus=\"app.eventBus\"\n :item=\"menuTarget.item\"\n :sidebarState=\"sidebarState\"\n :target=\"menuTarget.el\"\n :workspaceStore=\"app.store.value\"\n @closeMenu=\"closeMenu\"\n @showDeleteModal=\"deleteModalState.show()\" />\n <!-- Delete confirmation modal, triggered from the dropdown menu above. -->\n <ScalarModal\n v-if=\"menuTarget\"\n size=\"xxs\"\n :state=\"deleteModalState\"\n :title=\"`Delete ${menuTarget.item.title}`\">\n <DeleteSidebarListElement\n :variableName=\"menuTarget.item.title\"\n :warningMessage=\"deleteMessage\"\n @close=\"deleteModalState.hide()\"\n @delete=\"handleDelete\" />\n </ScalarModal>\n </template>\n </Resize>\n</template>\n\n<style scoped>\n/*\n * Gentle pulse for the registry loading skeletons. Matches the existing\n * `LoadingSkeleton.vue` easing/duration used in `@scalar/api-reference` so\n * any skeleton in the app feels consistent.\n */\n.sidebar-skeleton-row > span {\n animation: sidebar-skeleton-pulse 1.5s infinite alternate;\n}\n\n@keyframes sidebar-skeleton-pulse {\n from {\n opacity: 1;\n }\n to {\n opacity: 0.33;\n }\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { WorkspaceStore } from '@scalar/workspace-store/client';
|
|
2
|
+
import type { ImportDocumentFromRegistry } from '../../../../v2/types/configuration';
|
|
3
|
+
/** Result of attempting to load a registry document into the workspace store. */
|
|
4
|
+
type LoadRegistryDocumentResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
documentName: string;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
error: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace, slug, }: {
|
|
12
|
+
workspaceStore: WorkspaceStore;
|
|
13
|
+
fetcher: ImportDocumentFromRegistry;
|
|
14
|
+
namespace: string;
|
|
15
|
+
slug: string;
|
|
16
|
+
}) => Promise<LoadRegistryDocumentResult>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=load-registry-document.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-registry-document.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAGpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,iFAAiF;AACjF,KAAK,0BAA0B,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnG,eAAO,MAAM,oBAAoB,GAAU,+CAKxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb,KAAG,OAAO,CAAC,0BAA0B,CA2CrC,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { generateUniqueSlug } from "../../command-palette/helpers/generate-unique-slug.js";
|
|
2
|
+
import { coerce, object, string } from "@scalar/validation";
|
|
3
|
+
//#region src/v2/features/app/helpers/load-registry-document.ts
|
|
4
|
+
var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug }) => {
|
|
5
|
+
const documents = workspaceStore.workspace.documents;
|
|
6
|
+
const existing = Object.entries(documents).find(([, doc]) => {
|
|
7
|
+
const meta = doc?.["x-scalar-registry-meta"];
|
|
8
|
+
return meta?.namespace === namespace && meta?.slug === slug;
|
|
9
|
+
});
|
|
10
|
+
if (existing) return {
|
|
11
|
+
ok: true,
|
|
12
|
+
documentName: existing[0]
|
|
13
|
+
};
|
|
14
|
+
const result = await fetcher({
|
|
15
|
+
namespace,
|
|
16
|
+
slug,
|
|
17
|
+
version: "latest"
|
|
18
|
+
});
|
|
19
|
+
if (!result.ok) return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: `Failed to fetch document: ${result.error || "Unknown error"}`
|
|
22
|
+
};
|
|
23
|
+
const baseName = coerce(object({ info: object({ title: string() }) }), result.data).info.title;
|
|
24
|
+
const documentName = await generateUniqueSlug(baseName.trim() || slug, new Set(Object.keys(documents)));
|
|
25
|
+
if (!documentName) return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: "Failed to generate a unique name for the document"
|
|
28
|
+
};
|
|
29
|
+
await workspaceStore.addDocument({
|
|
30
|
+
name: documentName,
|
|
31
|
+
document: result.data,
|
|
32
|
+
meta: { "x-scalar-registry-meta": {
|
|
33
|
+
namespace,
|
|
34
|
+
slug
|
|
35
|
+
} }
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
ok: true,
|
|
39
|
+
documentName
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
//#endregion
|
|
43
|
+
export { loadRegistryDocument };
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=load-registry-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-registry-document.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"sourcesContent":["import { coerce, object, string } from '@scalar/validation'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport { generateUniqueSlug } from '@/v2/features/command-palette/helpers/generate-unique-slug'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\n/** Result of attempting to load a registry document into the workspace store. */\ntype LoadRegistryDocumentResult = { ok: true; documentName: string } | { ok: false; error: string }\n\nexport const loadRegistryDocument = async ({\n workspaceStore,\n fetcher,\n namespace,\n slug,\n}: {\n workspaceStore: WorkspaceStore\n fetcher: ImportDocumentFromRegistry\n namespace: string\n slug: string\n}): Promise<LoadRegistryDocumentResult> => {\n const documents = workspaceStore.workspace.documents\n\n const existing = Object.entries(documents).find(([, doc]) => {\n const meta = doc?.['x-scalar-registry-meta']\n return meta?.namespace === namespace && meta?.slug === slug\n })\n\n if (existing) {\n return { ok: true, documentName: existing[0] }\n }\n\n const result = await fetcher({ namespace, slug, version: 'latest' })\n if (!result.ok) {\n return {\n ok: false,\n error: `Failed to fetch document: ${result.error || 'Unknown error'}`,\n }\n }\n\n // Parse the document data into a schema\n const schema = object({ info: object({ title: string() }) })\n const baseName = coerce(schema, result.data).info.title\n\n const documentName = await generateUniqueSlug(baseName.trim() || slug, new Set(Object.keys(documents)))\n\n if (!documentName) {\n return {\n ok: false,\n error: 'Failed to generate a unique name for the document',\n }\n }\n\n // Add the document to the workspace store\n await workspaceStore.addDocument({\n name: documentName,\n document: result.data,\n meta: {\n 'x-scalar-registry-meta': { namespace, slug },\n },\n })\n\n return { ok: true, documentName }\n}\n"],"mappings":";;;AASA,IAAa,uBAAuB,OAAO,EACzC,gBACA,SACA,WACA,WAMyC;CACzC,MAAM,YAAY,eAAe,UAAU;CAE3C,MAAM,WAAW,OAAO,QAAQ,UAAU,CAAC,MAAM,GAAG,SAAS;EAC3D,MAAM,OAAO,MAAM;AACnB,SAAO,MAAM,cAAc,aAAa,MAAM,SAAS;GACvD;AAEF,KAAI,SACF,QAAO;EAAE,IAAI;EAAM,cAAc,SAAS;EAAI;CAGhD,MAAM,SAAS,MAAM,QAAQ;EAAE;EAAW;EAAM,SAAS;EAAU,CAAC;AACpE,KAAI,CAAC,OAAO,GACV,QAAO;EACL,IAAI;EACJ,OAAO,6BAA6B,OAAO,SAAS;EACrD;CAKH,MAAM,WAAW,OADF,OAAO,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,CAAC,EAAE,CAAC,EAC5B,OAAO,KAAK,CAAC,KAAK;CAElD,MAAM,eAAe,MAAM,mBAAmB,SAAS,MAAM,IAAI,MAAM,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,CAAC;AAEvG,KAAI,CAAC,aACH,QAAO;EACL,IAAI;EACJ,OAAO;EACR;AAIH,OAAM,eAAe,YAAY;EAC/B,MAAM;EACN,UAAU,OAAO;EACjB,MAAM,EACJ,0BAA0B;GAAE;GAAW;GAAM,EAC9C;EACF,CAAC;AAEF,QAAO;EAAE,IAAI;EAAM;EAAc"}
|
|
@@ -6,9 +6,9 @@ import type { WorkspaceEventBus } from '@scalar/workspace-store/events';
|
|
|
6
6
|
import type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments';
|
|
7
7
|
import type { WorkspaceDocument } from '@scalar/workspace-store/schemas/workspace';
|
|
8
8
|
import type { MaybeRefOrGetter } from 'vue';
|
|
9
|
-
import type { ApiClientOptions } from '../../../../v2/types/options';
|
|
10
9
|
import type { ImportDocumentFromRegistry } from '../../../../v2/types/configuration';
|
|
11
10
|
import type { ClientLayout } from '../../../../v2/types/layout';
|
|
11
|
+
import type { ApiClientOptions } from '../../../../v2/types/options';
|
|
12
12
|
/** These props are provided at the route level */
|
|
13
13
|
export type RouteProps = {
|
|
14
14
|
/** The slug of the currently selected document in the workspace */
|
|
@@ -144,6 +144,11 @@ export declare const ROUTES: ({
|
|
|
144
144
|
})[];
|
|
145
145
|
name?: undefined;
|
|
146
146
|
component?: undefined;
|
|
147
|
+
} | {
|
|
148
|
+
name: string;
|
|
149
|
+
path: string;
|
|
150
|
+
component: import("vue").DefineComponent<RouteProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<RouteProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
151
|
+
children?: undefined;
|
|
147
152
|
} | {
|
|
148
153
|
name: string;
|
|
149
154
|
path: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAA;AAEnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AACvE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAA;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,KAAK,CAAA;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import Authentication_default from "../../collection/components/Authentication.v
|
|
|
3
3
|
import Cookies_default from "../../collection/components/Cookies.vue.js";
|
|
4
4
|
import Editor_default from "../../collection/components/Editor/Editor.vue.js";
|
|
5
5
|
import Environment_default from "../../collection/components/Environment.vue.js";
|
|
6
|
+
import GetStarted_default from "../../collection/components/GetStarted.vue.js";
|
|
6
7
|
import Overview_default from "../../collection/components/Overview.vue.js";
|
|
7
8
|
import Runner_default from "../../collection/components/Runner/components/Runner.vue.js";
|
|
8
9
|
import Servers_default from "../../collection/components/Servers.vue.js";
|
|
@@ -16,116 +17,124 @@ import { mergeSearchParams } from "@scalar/helpers/url/merge-urls";
|
|
|
16
17
|
/** Routes for the API client app and web, the same as modal + workspace routes */
|
|
17
18
|
var ROUTES = [{
|
|
18
19
|
path: "/@:namespace/:workspaceSlug",
|
|
19
|
-
children: [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
path: "path/:pathEncoded/method/:method",
|
|
20
|
+
children: [
|
|
21
|
+
{
|
|
22
|
+
path: "document/:documentSlug",
|
|
23
23
|
children: [{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
path: "path/:pathEncoded/method/:method",
|
|
25
|
+
children: [{
|
|
26
|
+
name: "example",
|
|
27
|
+
path: "example/:exampleName",
|
|
28
|
+
component: Operation_default
|
|
29
|
+
}, {
|
|
30
|
+
name: "operation",
|
|
31
|
+
path: "",
|
|
32
|
+
component: OperationCollection_default,
|
|
33
|
+
redirect: { name: "operation.overview" },
|
|
34
|
+
children: [
|
|
35
|
+
{
|
|
36
|
+
name: "operation.overview",
|
|
37
|
+
path: "overview",
|
|
38
|
+
component: Overview_default
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "operation.servers",
|
|
42
|
+
path: "servers",
|
|
43
|
+
component: Servers_default
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "operation.authentication",
|
|
47
|
+
path: "authentication",
|
|
48
|
+
component: Authentication_default
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "operation.editor",
|
|
52
|
+
path: "editor",
|
|
53
|
+
component: Editor_default
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}]
|
|
27
57
|
}, {
|
|
28
|
-
name: "
|
|
58
|
+
name: "document",
|
|
29
59
|
path: "",
|
|
30
|
-
component:
|
|
31
|
-
redirect: { name: "operation.overview" },
|
|
60
|
+
component: DocumentCollection_default,
|
|
32
61
|
children: [
|
|
33
62
|
{
|
|
34
|
-
name: "
|
|
63
|
+
name: "document.redirect",
|
|
64
|
+
path: "",
|
|
65
|
+
redirect: { name: "document.overview" }
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "document.overview",
|
|
35
69
|
path: "overview",
|
|
36
70
|
component: Overview_default
|
|
37
71
|
},
|
|
38
72
|
{
|
|
39
|
-
name: "
|
|
73
|
+
name: "document.servers",
|
|
40
74
|
path: "servers",
|
|
41
75
|
component: Servers_default
|
|
42
76
|
},
|
|
43
77
|
{
|
|
44
|
-
name: "
|
|
78
|
+
name: "document.environment",
|
|
79
|
+
path: "environment",
|
|
80
|
+
component: Environment_default
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "document.authentication",
|
|
45
84
|
path: "authentication",
|
|
46
85
|
component: Authentication_default
|
|
47
86
|
},
|
|
48
87
|
{
|
|
49
|
-
name: "
|
|
50
|
-
path: "
|
|
51
|
-
component:
|
|
88
|
+
name: "document.cookies",
|
|
89
|
+
path: "cookies",
|
|
90
|
+
component: Cookies_default
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "document.scripts",
|
|
94
|
+
path: "scripts",
|
|
95
|
+
component: () => import("../../collection/components/Scripts.vue.js")
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: "document.runner",
|
|
99
|
+
path: "runner",
|
|
100
|
+
component: Runner_default
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "document.settings",
|
|
104
|
+
path: "settings",
|
|
105
|
+
component: Settings_default
|
|
52
106
|
}
|
|
53
107
|
]
|
|
54
108
|
}]
|
|
55
|
-
},
|
|
56
|
-
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "workspace.get-started",
|
|
112
|
+
path: "get-started",
|
|
113
|
+
component: GetStarted_default
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
name: "workspace",
|
|
57
117
|
path: "",
|
|
58
|
-
component:
|
|
118
|
+
component: WorkspaceCollection_default,
|
|
59
119
|
children: [
|
|
60
120
|
{
|
|
61
|
-
name: "
|
|
62
|
-
path: "",
|
|
63
|
-
redirect: { name: "document.overview" }
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: "document.overview",
|
|
67
|
-
path: "overview",
|
|
68
|
-
component: Overview_default
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: "document.servers",
|
|
72
|
-
path: "servers",
|
|
73
|
-
component: Servers_default
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: "document.environment",
|
|
121
|
+
name: "workspace.environment",
|
|
77
122
|
path: "environment",
|
|
78
123
|
component: Environment_default
|
|
79
124
|
},
|
|
80
125
|
{
|
|
81
|
-
name: "
|
|
82
|
-
path: "authentication",
|
|
83
|
-
component: Authentication_default
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: "document.cookies",
|
|
126
|
+
name: "workspace.cookies",
|
|
87
127
|
path: "cookies",
|
|
88
128
|
component: Cookies_default
|
|
89
129
|
},
|
|
90
130
|
{
|
|
91
|
-
name: "
|
|
92
|
-
path: "scripts",
|
|
93
|
-
component: () => import("../../collection/components/Scripts.vue.js")
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: "document.runner",
|
|
97
|
-
path: "runner",
|
|
98
|
-
component: Runner_default
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
name: "document.settings",
|
|
131
|
+
name: "workspace.settings",
|
|
102
132
|
path: "settings",
|
|
103
133
|
component: Settings_default
|
|
104
134
|
}
|
|
105
135
|
]
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
name: "workspace",
|
|
109
|
-
path: "",
|
|
110
|
-
component: WorkspaceCollection_default,
|
|
111
|
-
children: [
|
|
112
|
-
{
|
|
113
|
-
name: "workspace.environment",
|
|
114
|
-
path: "environment",
|
|
115
|
-
component: Environment_default
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
name: "workspace.cookies",
|
|
119
|
-
path: "cookies",
|
|
120
|
-
component: Cookies_default
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
name: "workspace.settings",
|
|
124
|
-
path: "settings",
|
|
125
|
-
component: Settings_default
|
|
126
|
-
}
|
|
127
|
-
]
|
|
128
|
-
}]
|
|
136
|
+
}
|
|
137
|
+
]
|
|
129
138
|
}, {
|
|
130
139
|
path: "/:pathMatch(.*)*",
|
|
131
140
|
redirect: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/routes.ts"],"sourcesContent":["import type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { mergeSearchParams } from '@scalar/helpers/url/merge-urls'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { Theme } from '@scalar/themes'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { WorkspaceDocument } from '@scalar/workspace-store/schemas/workspace'\nimport type { MaybeRefOrGetter } from 'vue'\nimport type { RouteRecordRaw } from 'vue-router'\n\nimport Authentication from '@/v2/features/collection/components/Authentication.vue'\nimport Cookies from '@/v2/features/collection/components/Cookies.vue'\nimport { Editor } from '@/v2/features/collection/components/Editor'\nimport Environment from '@/v2/features/collection/components/Environment.vue'\nimport Overview from '@/v2/features/collection/components/Overview.vue'\nimport { Runner } from '@/v2/features/collection/components/Runner'\nimport Servers from '@/v2/features/collection/components/Servers.vue'\nimport Settings from '@/v2/features/collection/components/Settings.vue'\nimport DocumentCollection from '@/v2/features/collection/DocumentCollection.vue'\nimport OperationCollection from '@/v2/features/collection/OperationCollection.vue'\nimport WorkspaceCollection from '@/v2/features/collection/WorkspaceCollection.vue'\nimport type { ApiClientOptions } from '@/v2/types/options'\nimport { Operation } from '@/v2/features/operation'\nimport { workspaceStorage } from '@/v2/helpers/storage'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\nimport type { ClientLayout } from '@/v2/types/layout'\n\n/** These props are provided at the route level */\nexport type RouteProps = {\n /** The slug of the currently selected document in the workspace */\n documentSlug: string\n /** The currently active document */\n document: WorkspaceDocument | null\n /** The workspace event bus */\n eventBus: WorkspaceEventBus\n /** The layout of the client */\n layout: ClientLayout\n /** The API path currently selected (e.g. \"/users/{id}\") */\n path?: string\n /** The HTTP method for the currently selected API path (e.g. GET, POST) */\n method?: HttpMethod\n /** The name of the currently selected example (for examples within an endpoint) */\n exampleName?: string\n /** The currently active environment */\n environment: XScalarEnvironment\n /** The workspace store */\n workspaceStore: WorkspaceStore\n /** The currently active workspace */\n activeWorkspace: { id: string; label: string }\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Custom themes available to the team */\n customThemes?: Theme[]\n /** The currently selected theme styles string */\n currentTheme?: string\n /** Whether the current color mode is dark */\n isDarkMode?: boolean\n /**\n * Fetches the full document from registry by meta. When provided, registry meta takes priority\n * over x-scalar-original-source-url when syncing. Returns the document as a plain object.\n */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n /** Whether telemetry is enabled */\n telemetry?: boolean\n /** Updates the telemetry enabled state */\n onUpdateTelemetry?: (value: boolean) => void\n /** App or modal options forwarded to operation/auth blocks */\n options?: MaybeRefOrGetter<ApiClientOptions>\n}\n\n/** When in the collections pages */\nexport type CollectionProps = RouteProps &\n (\n | {\n collectionType: 'document' | 'operation'\n document: WorkspaceDocument\n }\n | {\n collectionType: 'workspace'\n document: null\n }\n )\n\nexport type ScalarClientAppRouteParams =\n | 'namespace'\n | 'workspaceSlug'\n | 'documentSlug'\n | 'pathEncoded'\n | 'method'\n | 'exampleName'\n\n/** Routes for the API client app and web, the same as modal + workspace routes */\nexport const ROUTES = [\n {\n path: '/@:namespace/:workspaceSlug',\n children: [\n {\n path: 'document/:documentSlug',\n children: [\n // Example page\n {\n path: 'path/:pathEncoded/method/:method',\n children: [\n {\n name: 'example',\n path: 'example/:exampleName',\n component: Operation,\n },\n {\n name: 'operation',\n path: '',\n component: OperationCollection,\n redirect: {\n name: 'operation.overview',\n },\n children: [\n {\n name: 'operation.overview',\n path: 'overview',\n component: Overview,\n },\n {\n name: 'operation.servers',\n path: 'servers',\n component: Servers,\n },\n {\n name: 'operation.authentication',\n path: 'authentication',\n component: Authentication,\n },\n {\n name: 'operation.editor',\n path: 'editor',\n component: Editor,\n },\n ],\n },\n ],\n },\n // Document Page\n {\n name: 'document',\n path: '',\n component: DocumentCollection,\n children: [\n // Redirect to overview\n {\n name: 'document.redirect',\n path: '',\n redirect: {\n name: 'document.overview',\n },\n },\n // Document overview\n {\n name: 'document.overview',\n path: 'overview',\n component: Overview,\n },\n // Document servers\n {\n name: 'document.servers',\n path: 'servers',\n component: Servers,\n },\n // Document environment\n {\n name: 'document.environment',\n path: 'environment',\n component: Environment,\n },\n // Document authentication\n {\n name: 'document.authentication',\n path: 'authentication',\n component: Authentication,\n },\n // Document cookies\n {\n name: 'document.cookies',\n path: 'cookies',\n component: Cookies,\n },\n // Document scripts (pre-request / post-response)\n {\n name: 'document.scripts',\n path: 'scripts',\n component: () => import('@/v2/features/collection/components/Scripts.vue'),\n },\n // Document runner\n {\n name: 'document.runner',\n path: 'runner',\n component: Runner,\n },\n // Document settings\n {\n name: 'document.settings',\n path: 'settings',\n component: Settings,\n },\n ],\n },\n ],\n },\n // Workspace page\n {\n name: 'workspace',\n path: '',\n component: WorkspaceCollection,\n children: [\n // Workspace environment\n {\n name: 'workspace.environment',\n path: 'environment',\n component: Environment,\n },\n // Workspace cookies\n {\n name: 'workspace.cookies',\n path: 'cookies',\n component: Cookies,\n },\n // Workspace settings\n {\n name: 'workspace.settings',\n path: 'settings',\n component: Settings,\n },\n ],\n },\n ],\n },\n {\n path: '/:pathMatch(.*)*',\n redirect: () => {\n const DEFAULT_PATH = '/@local/default/document/drafts/overview'\n const lastPath = workspaceStorage.getLastPath() ?? DEFAULT_PATH\n\n // Set the default path to the last path so we don't go to an inifite loop if the last path is invalid\n workspaceStorage.setCurrentPath(DEFAULT_PATH)\n\n const url = new URL(lastPath, 'http://example.com')\n\n const queryParameters = new URLSearchParams(window.location.search)\n\n //Merge the query parameters with the last path\n const mergedSearchParams = mergeSearchParams(url.searchParams, queryParameters)\n\n // Preserve all query paramters\n return `${url.pathname}?${mergedSearchParams.toString()}`\n },\n },\n] satisfies RouteRecordRaw[]\n"],"mappings":";;;;;;;;;;;;;;;;AA6FA,IAAa,SAAS,CACpB;CACE,MAAM;CACN,UAAU,CACR;EACE,MAAM;EACN,UAAU,CAER;GACE,MAAM;GACN,UAAU,CACR;IACE,MAAM;IACN,MAAM;IACN,WAAW;IACZ,EACD;IACE,MAAM;IACN,MAAM;IACN,WAAW;IACX,UAAU,EACR,MAAM,sBACP;IACD,UAAU;KACR;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KACD;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KACD;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KACD;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KACF;IACF,CACF;GACF,EAED;GACE,MAAM;GACN,MAAM;GACN,WAAW;GACX,UAAU;IAER;KACE,MAAM;KACN,MAAM;KACN,UAAU,EACR,MAAM,qBACP;KACF;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,iBAAiB,OAAO;KACzB;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IACF;GACF,CACF;EACF,EAED;EACE,MAAM;EACN,MAAM;EACN,WAAW;EACX,UAAU;GAER;IACE,MAAM;IACN,MAAM;IACN,WAAW;IACZ;GAED;IACE,MAAM;IACN,MAAM;IACN,WAAW;IACZ;GAED;IACE,MAAM;IACN,MAAM;IACN,WAAW;IACZ;GACF;EACF,CACF;CACF,EACD;CACE,MAAM;CACN,gBAAgB;EACd,MAAM,eAAe;EACrB,MAAM,WAAW,iBAAiB,aAAa,IAAI;AAGnD,mBAAiB,eAAe,aAAa;EAE7C,MAAM,MAAM,IAAI,IAAI,UAAU,qBAAqB;EAEnD,MAAM,kBAAkB,IAAI,gBAAgB,OAAO,SAAS,OAAO;EAGnE,MAAM,qBAAqB,kBAAkB,IAAI,cAAc,gBAAgB;AAG/E,SAAO,GAAG,IAAI,SAAS,GAAG,mBAAmB,UAAU;;CAE1D,CACF"}
|
|
1
|
+
{"version":3,"file":"routes.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/routes.ts"],"sourcesContent":["import type { HttpMethod } from '@scalar/helpers/http/http-methods'\nimport { mergeSearchParams } from '@scalar/helpers/url/merge-urls'\nimport type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { Theme } from '@scalar/themes'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { WorkspaceDocument } from '@scalar/workspace-store/schemas/workspace'\nimport type { MaybeRefOrGetter } from 'vue'\nimport type { RouteRecordRaw } from 'vue-router'\n\nimport Authentication from '@/v2/features/collection/components/Authentication.vue'\nimport Cookies from '@/v2/features/collection/components/Cookies.vue'\nimport { Editor } from '@/v2/features/collection/components/Editor'\nimport Environment from '@/v2/features/collection/components/Environment.vue'\nimport GetStarted from '@/v2/features/collection/components/GetStarted.vue'\nimport Overview from '@/v2/features/collection/components/Overview.vue'\nimport { Runner } from '@/v2/features/collection/components/Runner'\nimport Servers from '@/v2/features/collection/components/Servers.vue'\nimport Settings from '@/v2/features/collection/components/Settings.vue'\nimport DocumentCollection from '@/v2/features/collection/DocumentCollection.vue'\nimport OperationCollection from '@/v2/features/collection/OperationCollection.vue'\nimport WorkspaceCollection from '@/v2/features/collection/WorkspaceCollection.vue'\nimport { Operation } from '@/v2/features/operation'\nimport { workspaceStorage } from '@/v2/helpers/storage'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\nimport type { ClientLayout } from '@/v2/types/layout'\nimport type { ApiClientOptions } from '@/v2/types/options'\n\n/** These props are provided at the route level */\nexport type RouteProps = {\n /** The slug of the currently selected document in the workspace */\n documentSlug: string\n /** The currently active document */\n document: WorkspaceDocument | null\n /** The workspace event bus */\n eventBus: WorkspaceEventBus\n /** The layout of the client */\n layout: ClientLayout\n /** The API path currently selected (e.g. \"/users/{id}\") */\n path?: string\n /** The HTTP method for the currently selected API path (e.g. GET, POST) */\n method?: HttpMethod\n /** The name of the currently selected example (for examples within an endpoint) */\n exampleName?: string\n /** The currently active environment */\n environment: XScalarEnvironment\n /** The workspace store */\n workspaceStore: WorkspaceStore\n /** The currently active workspace */\n activeWorkspace: { id: string; label: string }\n /** Client plugins */\n plugins: ClientPlugin[]\n /** Custom themes available to the team */\n customThemes?: Theme[]\n /** The currently selected theme styles string */\n currentTheme?: string\n /** Whether the current color mode is dark */\n isDarkMode?: boolean\n /**\n * Fetches the full document from registry by meta. When provided, registry meta takes priority\n * over x-scalar-original-source-url when syncing. Returns the document as a plain object.\n */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n /** Whether telemetry is enabled */\n telemetry?: boolean\n /** Updates the telemetry enabled state */\n onUpdateTelemetry?: (value: boolean) => void\n /** App or modal options forwarded to operation/auth blocks */\n options?: MaybeRefOrGetter<ApiClientOptions>\n}\n\n/** When in the collections pages */\nexport type CollectionProps = RouteProps &\n (\n | {\n collectionType: 'document' | 'operation'\n document: WorkspaceDocument\n }\n | {\n collectionType: 'workspace'\n document: null\n }\n )\n\nexport type ScalarClientAppRouteParams =\n | 'namespace'\n | 'workspaceSlug'\n | 'documentSlug'\n | 'pathEncoded'\n | 'method'\n | 'exampleName'\n\n/** Routes for the API client app and web, the same as modal + workspace routes */\nexport const ROUTES = [\n {\n path: '/@:namespace/:workspaceSlug',\n children: [\n {\n path: 'document/:documentSlug',\n children: [\n // Example page\n {\n path: 'path/:pathEncoded/method/:method',\n children: [\n {\n name: 'example',\n path: 'example/:exampleName',\n component: Operation,\n },\n {\n name: 'operation',\n path: '',\n component: OperationCollection,\n redirect: {\n name: 'operation.overview',\n },\n children: [\n {\n name: 'operation.overview',\n path: 'overview',\n component: Overview,\n },\n {\n name: 'operation.servers',\n path: 'servers',\n component: Servers,\n },\n {\n name: 'operation.authentication',\n path: 'authentication',\n component: Authentication,\n },\n {\n name: 'operation.editor',\n path: 'editor',\n component: Editor,\n },\n ],\n },\n ],\n },\n // Document Page\n {\n name: 'document',\n path: '',\n component: DocumentCollection,\n children: [\n // Redirect to overview\n {\n name: 'document.redirect',\n path: '',\n redirect: {\n name: 'document.overview',\n },\n },\n // Document overview\n {\n name: 'document.overview',\n path: 'overview',\n component: Overview,\n },\n // Document servers\n {\n name: 'document.servers',\n path: 'servers',\n component: Servers,\n },\n // Document environment\n {\n name: 'document.environment',\n path: 'environment',\n component: Environment,\n },\n // Document authentication\n {\n name: 'document.authentication',\n path: 'authentication',\n component: Authentication,\n },\n // Document cookies\n {\n name: 'document.cookies',\n path: 'cookies',\n component: Cookies,\n },\n // Document scripts (pre-request / post-response)\n {\n name: 'document.scripts',\n path: 'scripts',\n component: () => import('@/v2/features/collection/components/Scripts.vue'),\n },\n // Document runner\n {\n name: 'document.runner',\n path: 'runner',\n component: Runner,\n },\n // Document settings\n {\n name: 'document.settings',\n path: 'settings',\n component: Settings,\n },\n ],\n },\n ],\n },\n // Workspace get started page (standalone, no workspace header or tabs)\n {\n name: 'workspace.get-started',\n path: 'get-started',\n component: GetStarted,\n },\n // Workspace page\n {\n name: 'workspace',\n path: '',\n component: WorkspaceCollection,\n children: [\n // Workspace environment\n {\n name: 'workspace.environment',\n path: 'environment',\n component: Environment,\n },\n // Workspace cookies\n {\n name: 'workspace.cookies',\n path: 'cookies',\n component: Cookies,\n },\n // Workspace settings\n {\n name: 'workspace.settings',\n path: 'settings',\n component: Settings,\n },\n ],\n },\n ],\n },\n {\n path: '/:pathMatch(.*)*',\n redirect: () => {\n const DEFAULT_PATH = '/@local/default/document/drafts/overview'\n const lastPath = workspaceStorage.getLastPath() ?? DEFAULT_PATH\n\n // Set the default path to the last path so we don't go to an inifite loop if the last path is invalid\n workspaceStorage.setCurrentPath(DEFAULT_PATH)\n\n const url = new URL(lastPath, 'http://example.com')\n\n const queryParameters = new URLSearchParams(window.location.search)\n\n //Merge the query parameters with the last path\n const mergedSearchParams = mergeSearchParams(url.searchParams, queryParameters)\n\n // Preserve all query paramters\n return `${url.pathname}?${mergedSearchParams.toString()}`\n },\n },\n] satisfies RouteRecordRaw[]\n"],"mappings":";;;;;;;;;;;;;;;;;AA8FA,IAAa,SAAS,CACpB;CACE,MAAM;CACN,UAAU;EACR;GACE,MAAM;GACN,UAAU,CAER;IACE,MAAM;IACN,UAAU,CACR;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ,EACD;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACX,UAAU,EACR,MAAM,sBACP;KACD,UAAU;MACR;OACE,MAAM;OACN,MAAM;OACN,WAAW;OACZ;MACD;OACE,MAAM;OACN,MAAM;OACN,WAAW;OACZ;MACD;OACE,MAAM;OACN,MAAM;OACN,WAAW;OACZ;MACD;OACE,MAAM;OACN,MAAM;OACN,WAAW;OACZ;MACF;KACF,CACF;IACF,EAED;IACE,MAAM;IACN,MAAM;IACN,WAAW;IACX,UAAU;KAER;MACE,MAAM;MACN,MAAM;MACN,UAAU,EACR,MAAM,qBACP;MACF;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KAED;MACE,MAAM;MACN,MAAM;MACN,iBAAiB,OAAO;MACzB;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KAED;MACE,MAAM;MACN,MAAM;MACN,WAAW;MACZ;KACF;IACF,CACF;GACF;EAED;GACE,MAAM;GACN,MAAM;GACN,WAAW;GACZ;EAED;GACE,MAAM;GACN,MAAM;GACN,WAAW;GACX,UAAU;IAER;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IAED;KACE,MAAM;KACN,MAAM;KACN,WAAW;KACZ;IACF;GACF;EACF;CACF,EACD;CACE,MAAM;CACN,gBAAgB;EACd,MAAM,eAAe;EACrB,MAAM,WAAW,iBAAiB,aAAa,IAAI;AAGnD,mBAAiB,eAAe,aAAa;EAE7C,MAAM,MAAM,IAAI,IAAI,UAAU,qBAAqB;EAEnD,MAAM,kBAAkB,IAAI,gBAAgB,OAAO,SAAS,OAAO;EAGnE,MAAM,qBAAqB,kBAAkB,IAAI,cAAc,gBAAgB;AAG/E,SAAO,GAAG,IAAI,SAAS,GAAG,mBAAmB,UAAU;;CAE1D,CACF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
/**
|
|
3
|
+
* Minimum shape a filterable item needs. Anything we run through this hook
|
|
4
|
+
* must have at least a `title`, which is the only field the fuzzy index
|
|
5
|
+
* looks at. Extra fields on `T` are preserved verbatim in the filtered list.
|
|
6
|
+
*/
|
|
7
|
+
type FilterableItem = {
|
|
8
|
+
title: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Reactive filter state for a list of sidebar documents.
|
|
12
|
+
*
|
|
13
|
+
* Extracts the "jump to document by name" affordance used by the top-level
|
|
14
|
+
* sidebar view into a stand-alone, testable primitive. Callers provide a
|
|
15
|
+
* reactive source of items (ref, computed, or getter) and receive:
|
|
16
|
+
*
|
|
17
|
+
* - `isVisible`: whether the filter input is currently revealed
|
|
18
|
+
* - `query`: v-model target for the search input
|
|
19
|
+
* - `filteredItems`: the input list narrowed down by the current query
|
|
20
|
+
* - `toggle()`: flips `isVisible`; clears the query when hiding so the list
|
|
21
|
+
* is not left in a filtered state after the input disappears
|
|
22
|
+
* - `reset()`: force-hides the filter and clears the query, e.g. when the
|
|
23
|
+
* user drills into a document and the top-level filter no longer applies
|
|
24
|
+
*
|
|
25
|
+
* The Fuse index is rebuilt whenever the input list changes so newly added
|
|
26
|
+
* or removed entries are reflected immediately. We deliberately index only
|
|
27
|
+
* the `title` because this is a lightweight UI-level filter, not a content
|
|
28
|
+
* search.
|
|
29
|
+
*/
|
|
30
|
+
export declare const useDocumentFilter: <T extends FilterableItem>(items: MaybeRefOrGetter<T[]>) => {
|
|
31
|
+
isVisible: import("vue").Ref<boolean, boolean>;
|
|
32
|
+
query: import("vue").Ref<string, string>;
|
|
33
|
+
filteredItems: import("vue").ComputedRef<T[]>;
|
|
34
|
+
toggle: () => void;
|
|
35
|
+
reset: () => void;
|
|
36
|
+
};
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=use-document-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-document-filter.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/hooks/use-document-filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAA0B,MAAM,KAAK,CAAA;AAEnE;;;;GAIG;AACH,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,cAAc,EAAE,OAAO,gBAAgB,CAAC,CAAC,EAAE,CAAC;;;;;;CA0CvF,CAAA"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { computed, ref, toValue } from "vue";
|
|
2
|
+
import Fuse from "fuse.js";
|
|
3
|
+
//#region src/v2/features/app/hooks/use-document-filter.ts
|
|
4
|
+
/**
|
|
5
|
+
* Reactive filter state for a list of sidebar documents.
|
|
6
|
+
*
|
|
7
|
+
* Extracts the "jump to document by name" affordance used by the top-level
|
|
8
|
+
* sidebar view into a stand-alone, testable primitive. Callers provide a
|
|
9
|
+
* reactive source of items (ref, computed, or getter) and receive:
|
|
10
|
+
*
|
|
11
|
+
* - `isVisible`: whether the filter input is currently revealed
|
|
12
|
+
* - `query`: v-model target for the search input
|
|
13
|
+
* - `filteredItems`: the input list narrowed down by the current query
|
|
14
|
+
* - `toggle()`: flips `isVisible`; clears the query when hiding so the list
|
|
15
|
+
* is not left in a filtered state after the input disappears
|
|
16
|
+
* - `reset()`: force-hides the filter and clears the query, e.g. when the
|
|
17
|
+
* user drills into a document and the top-level filter no longer applies
|
|
18
|
+
*
|
|
19
|
+
* The Fuse index is rebuilt whenever the input list changes so newly added
|
|
20
|
+
* or removed entries are reflected immediately. We deliberately index only
|
|
21
|
+
* the `title` because this is a lightweight UI-level filter, not a content
|
|
22
|
+
* search.
|
|
23
|
+
*/
|
|
24
|
+
var useDocumentFilter = (items) => {
|
|
25
|
+
const isVisible = ref(false);
|
|
26
|
+
const query = ref("");
|
|
27
|
+
const fuse = computed(() => new Fuse(toValue(items), {
|
|
28
|
+
keys: ["title"],
|
|
29
|
+
threshold: .3,
|
|
30
|
+
ignoreLocation: true
|
|
31
|
+
}));
|
|
32
|
+
const filteredItems = computed(() => {
|
|
33
|
+
const trimmed = query.value.trim();
|
|
34
|
+
if (!trimmed) return toValue(items);
|
|
35
|
+
return fuse.value.search(trimmed).map((result) => result.item);
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Flip the filter input's visibility. Hiding the input also clears the
|
|
39
|
+
* current query so the list does not stay filtered after the input
|
|
40
|
+
* disappears (which would otherwise leave documents invisible with no way
|
|
41
|
+
* for the user to see why).
|
|
42
|
+
*/
|
|
43
|
+
const toggle = () => {
|
|
44
|
+
isVisible.value = !isVisible.value;
|
|
45
|
+
if (!isVisible.value) query.value = "";
|
|
46
|
+
};
|
|
47
|
+
/** Hide the input and clear the query. */
|
|
48
|
+
const reset = () => {
|
|
49
|
+
isVisible.value = false;
|
|
50
|
+
query.value = "";
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
isVisible,
|
|
54
|
+
query,
|
|
55
|
+
filteredItems,
|
|
56
|
+
toggle,
|
|
57
|
+
reset
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { useDocumentFilter };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=use-document-filter.js.map
|