@scalar/api-client 3.6.0 → 3.6.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 +11 -0
- package/dist/style.css +31 -14
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- 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 +30 -28
- package/dist/v2/features/app/App.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.script.js +3 -2
- package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppHeaderActions.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppHeaderActions.vue.script.js +12 -10
- package/dist/v2/features/app/components/AppHeaderActions.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts +0 -2
- 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 +3 -7
- package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/DesktopTabs.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/DesktopTabs.vue.js.map +1 -1
- package/dist/v2/features/app/components/DesktopTabs.vue.script.js +2 -2
- package/dist/v2/features/app/components/DesktopTabs.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js.map +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.script.js +35 -17
- package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.script.js +15 -13
- package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.js +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.script.js +51 -39
- package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js +5 -5
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.script.js +44 -42
- package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.script.js +33 -17
- package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/helpers/load-document-from-source.d.ts.map +1 -1
- package/dist/v2/features/command-palette/helpers/load-document-from-source.js +3 -2
- package/dist/v2/features/command-palette/helpers/load-document-from-source.js.map +1 -1
- package/dist/v2/helpers/is-url.d.ts.map +1 -1
- package/dist/v2/helpers/is-url.js +2 -1
- package/dist/v2/helpers/is-url.js.map +1 -1
- package/dist/vue-styles.css +6 -6
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteRequest.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteRequest.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Request Component\n *\n * Provides a form for creating a new API request (operation) in a document.\n * Users can specify the request path, HTTP method, document (collection),\n * and optionally assign it to a tag.\n *\n * Validates that no operation with the same path and method already exists\n * in the selected document to prevent duplicates.\n *\n * @example\n * <CommandPaletteRequest\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {\n name: 'CommandPaletteRequest',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarDropdown,\n ScalarDropdownItem,\n ScalarIcon,\n ScalarListbox,\n} from '@scalar/components'\nimport {\n HTTP_METHODS,\n type HttpMethod,\n} from '@scalar/helpers/http/http-methods'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, watch } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport HttpMethodBadge from '@/v2/blocks/operation-code-sample/components/HttpMethod.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus, documentName, tagId } = defineProps<{\n /** The workspace store for accessing documents and operations */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting operation creation events */\n eventBus: WorkspaceEventBus\n /** Preselected document id to create the request in */\n documentName?: string\n /** Preselected tag id to add the request to (optional) */\n tagId?: string\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the request is created successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\n/** HTTP method option type for selectors */\ntype MethodOption = {\n id: string\n label: string\n method: HttpMethod\n}\n\n/** Tag option type for selectors */\ntype TagOption = {\n id: string\n label: string\n}\n\nconst router = useRouter()\n\nconst requestPath = ref('/')\nconst requestPathTrimmed = computed(() => requestPath.value.trim())\n\n/** All available documents (collections) in the workspace */\nconst availableDocuments = computed(() =>\n Object.entries(workspaceStore.workspace.documents).map(\n ([name, document]) => ({\n id: name,\n label: document.info.title || name,\n }),\n ),\n)\n\n/** Available HTTP methods for the dropdown (GET, POST, PUT, etc.) */\nconst availableMethods: MethodOption[] = HTTP_METHODS.map((method) => ({\n id: method,\n label: method.toUpperCase(),\n method,\n}))\n\nconst selectedDocument = ref<{ id: string; label: string } | undefined>(\n documentName\n ? availableDocuments.value.find((document) => document.id === documentName)\n : (availableDocuments.value[0] ?? undefined),\n)\n\nconst selectedMethod = ref<MethodOption | undefined>(\n availableMethods.find((method) => method.method === 'get'),\n)\n\n/**\n * All available tags for the selected document.\n * Includes a \"No Tag\" option for operations without a tag assignment.\n */\nconst availableTags = computed<TagOption[]>(() => {\n if (!selectedDocument.value) {\n return []\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n if (!document) {\n return []\n }\n\n return [\n { id: '', label: 'No Tag' },\n ...(document.tags?.map((tag) => ({\n id: tag.name,\n label: tag.name,\n })) ?? []),\n ]\n})\n\nconst selectedTag = ref<TagOption | undefined>(\n tagId ? availableTags.value.find((tag) => tag.id === tagId) : undefined,\n)\n\n// Reset the selected tag to the \"No Tag\" option when the document changes\nwatch(selectedDocument, () => {\n selectedTag.value = availableTags.value.find((tag) => tag.id === '')\n})\n\n/**\n * Check if an operation with the same path and method already exists.\n * Used to prevent creating duplicate operations.\n */\nconst operationExists = computed<boolean>(() => {\n if (\n !selectedDocument.value ||\n !selectedMethod.value ||\n !requestPathTrimmed.value\n ) {\n return false\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n\n /** Ensure path starts with '/' for consistent lookup */\n const normalizedPath = requestPathTrimmed.value.startsWith('/')\n ? requestPathTrimmed.value\n : `/${requestPathTrimmed.value}`\n\n return !!document?.paths?.[normalizedPath]?.[selectedMethod.value.method]\n})\n\n/**\n * Check if the form should be disabled.\n * Disabled when any required field is missing or operation already exists.\n */\nconst isDisabled = computed<boolean>(() => {\n if (\n !requestPathTrimmed.value ||\n !selectedDocument.value ||\n !selectedMethod.value\n ) {\n return true\n }\n\n /** Prevent creating duplicate operations */\n if (operationExists.value) {\n return true\n }\n\n return false\n})\n\n/** Handle HTTP method selection from dropdown */\nconst handleSelectMethod = (method: MethodOption | undefined): void => {\n if (method) {\n selectedMethod.value = method\n }\n}\n\n/** Handle tag selection from dropdown */\nconst handleSelectTag = (tag: TagOption | undefined): void => {\n if (tag) {\n selectedTag.value = tag\n }\n}\n\n/**\n * Create the request and close the command palette.\n * Emits an event to create a new operation with the specified details.\n */\nconst handleSubmit = (): void => {\n if (isDisabled.value || !selectedDocument.value || !selectedMethod.value) {\n return\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n\n if (!document) {\n return\n }\n\n eventBus.emit('operation:create:operation', {\n documentName: selectedDocument.value.id,\n path: requestPathTrimmed.value,\n method: selectedMethod.value.method,\n operation: {\n tags: selectedTag.value?.id ? [selectedTag.value.id] : undefined,\n },\n callback: (success) => {\n if (success) {\n /** Build the sidebar */\n workspaceStore.buildSidebar(selectedDocument.value?.id ?? '')\n\n const path = requestPathTrimmed.value.startsWith('/')\n ? requestPathTrimmed.value\n : `/${requestPathTrimmed.value}`\n\n /** Navigate to the example */\n router.push({\n name: 'example',\n params: {\n documentSlug: selectedDocument.value?.id,\n pathEncoded: encodeURIComponent(path),\n method: selectedMethod.value?.method,\n exampleName: 'default',\n },\n })\n }\n },\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n emit('back', event)\n}\n</script>\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <!-- Request path input -->\n <CommandActionInput\n v-model=\"requestPath\"\n label=\"Request Path\"\n placeholder=\"/users\"\n @delete=\"handleBack\" />\n\n <!-- Selectors for document, method, and tag -->\n <template #options>\n <div class=\"flex flex-1 gap-1\">\n <!-- Document (collection) selector -->\n <ScalarListbox\n v-model=\"selectedDocument\"\n :options=\"availableDocuments\">\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-[150px] min-w-[150px] justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <span :class=\"selectedDocument ? 'text-c-1 truncate' : 'text-c-3'\">\n {{\n selectedDocument ? selectedDocument.label : 'Select Document'\n }}\n </span>\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n\n <!-- HTTP method selector (GET, POST, PUT, etc.) -->\n <ScalarDropdown\n placement=\"bottom\"\n resize>\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-[100px] min-w-[100px] justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <div class=\"flex items-center gap-2\">\n <HttpMethodBadge\n v-if=\"selectedMethod\"\n :method=\"selectedMethod.method\" />\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </div>\n </ScalarButton>\n\n <!-- Dropdown list of all HTTP methods -->\n <template #items>\n <div class=\"custom-scroll max-h-40\">\n <ScalarDropdownItem\n v-for=\"method in availableMethods\"\n :key=\"method.id\"\n class=\"flex h-7 w-full items-center justify-center px-1\"\n @click=\"handleSelectMethod(method)\">\n <HttpMethodBadge :method=\"method.method\" />\n </ScalarDropdownItem>\n </div>\n </template>\n </ScalarDropdown>\n\n <!-- Tag selector (optional) for organizing operations -->\n <ScalarDropdown\n placement=\"bottom\"\n resize>\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-full justify-between gap-1 p-2 text-xs\"\n :disabled=\"!availableTags.length\"\n variant=\"outlined\">\n <span :class=\"selectedTag ? 'text-c-1 truncate' : 'text-c-3'\">\n {{ selectedTag ? selectedTag.label : 'Select Tag (Optional)' }}\n </span>\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n\n <!-- Dropdown list of available tags -->\n <template #items>\n <div class=\"custom-scroll max-h-40\">\n <ScalarDropdownItem\n v-for=\"tag in availableTags\"\n :key=\"tag.id\"\n class=\"flex h-7 w-full items-center px-1\"\n @click=\"handleSelectTag(tag)\">\n <span class=\"truncate\">{{ tag.label }}</span>\n </ScalarDropdownItem>\n </div>\n </template>\n </ScalarDropdown>\n </div>\n </template>\n\n <template #submit>Create Request</template>\n </CommandActionForm>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CommandPaletteRequest.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteRequest.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Request Component\n *\n * Provides a form for creating a new API request (operation) in a document.\n * Users can specify the request path, HTTP method, document (collection),\n * and optionally assign it to a tag.\n *\n * Validates that no operation with the same path and method already exists\n * in the selected document to prevent duplicates.\n *\n * @example\n * <CommandPaletteRequest\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {\n name: 'CommandPaletteRequest',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarDropdown,\n ScalarDropdownItem,\n ScalarIcon,\n ScalarListbox,\n} from '@scalar/components'\nimport {\n HTTP_METHODS,\n type HttpMethod,\n} from '@scalar/helpers/http/http-methods'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, watch, type ComputedRef } from 'vue'\n\nimport HttpMethodBadge from '@/v2/blocks/operation-code-sample/components/HttpMethod.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus, documentName, tagId } = defineProps<{\n /** The workspace store for accessing documents and operations */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting operation creation events */\n eventBus: WorkspaceEventBus\n /** Preselected document id to create the request in */\n documentName?: string\n /** Preselected tag id to add the request to (optional) */\n tagId?: string\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the request is created successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\n/** HTTP method option type for selectors */\ntype MethodOption = {\n id: string\n label: string\n method: HttpMethod\n}\n\n/** Tag option type for selectors */\ntype TagOption = {\n id: string\n label: string\n}\n\nconst requestPath = ref('/')\nconst requestPathTrimmed = computed(() => requestPath.value.trim())\n\n/** Ensure path starts with '/' for consistent lookup */\nconst normalizedRequestPath = computed<string>(() =>\n requestPathTrimmed.value.startsWith('/')\n ? requestPathTrimmed.value\n : `/${requestPathTrimmed.value}`,\n)\n\n/** All available documents (collections) in the workspace */\nconst availableDocuments = computed(() =>\n Object.entries(workspaceStore.workspace.documents).map(\n ([name, document]) => ({\n id: name,\n label: document.info.title || name,\n }),\n ),\n)\n\n/** Available HTTP methods for the dropdown (GET, POST, PUT, etc.) */\nconst availableMethods: MethodOption[] = HTTP_METHODS.map((method) => ({\n id: method,\n label: method.toUpperCase(),\n method,\n}))\n\nconst selectedDocument = ref<{ id: string; label: string } | undefined>(\n documentName\n ? availableDocuments.value.find((document) => document.id === documentName)\n : (availableDocuments.value[0] ?? undefined),\n)\n\nconst selectedMethod = ref<MethodOption | undefined>(\n availableMethods.find((method) => method.method === 'get'),\n)\n\n/**\n * All available tags for the selected document.\n * Includes a \"No Tag\" option for operations without a tag assignment.\n */\nconst availableTags = computed<TagOption[]>(() => {\n if (!selectedDocument.value) {\n return []\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n if (!document) {\n return []\n }\n\n return [\n { id: '', label: 'No Tag' },\n ...(document.tags?.map((tag) => ({\n id: tag.name,\n label: tag.name,\n })) ?? []),\n ]\n})\n\nconst selectedTag = ref<TagOption | undefined>(\n tagId ? availableTags.value.find((tag) => tag.id === tagId) : undefined,\n)\n\n// Reset the selected tag to the \"No Tag\" option when the document changes\nwatch(selectedDocument, () => {\n selectedTag.value = availableTags.value.find((tag) => tag.id === '')\n})\n\n/**\n * Validation message surfaced under the input.\n *\n * Resolves to `null` when the form is valid; otherwise to a human-readable\n * reason the user can act on. Empty input is the default state so we keep\n * the field free of error styling — `isDisabled` still blocks submission\n * there, matching the {@link CommandPaletteOpenApiDocument} pattern.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (\n !requestPathTrimmed.value ||\n !selectedDocument.value ||\n !selectedMethod.value\n ) {\n return null\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n const method = selectedMethod.value.method\n\n if (document?.paths?.[normalizedRequestPath.value]?.[method]) {\n return `A ${method.toUpperCase()} operation at \"${normalizedRequestPath.value}\" already exists in \"${selectedDocument.value.label}\". Try a different path or method.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while required fields are missing or a duplicate exists.\n * The inline `errorMessage` explains the duplicate case so the user knows how\n * to recover instead of facing a silently disabled button.\n */\nconst isDisabled = computed<boolean>(\n () =>\n !requestPathTrimmed.value ||\n !selectedDocument.value ||\n !selectedMethod.value ||\n errorMessage.value !== null,\n)\n\n/** Handle HTTP method selection from dropdown */\nconst handleSelectMethod = (method: MethodOption | undefined): void => {\n if (method) {\n selectedMethod.value = method\n }\n}\n\n/** Handle tag selection from dropdown */\nconst handleSelectTag = (tag: TagOption | undefined): void => {\n if (tag) {\n selectedTag.value = tag\n }\n}\n\n/**\n * Create the request and close the command palette.\n * Emits an event to create a new operation with the specified details.\n */\nconst handleSubmit = (): void => {\n if (isDisabled.value || !selectedDocument.value || !selectedMethod.value) {\n return\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n\n if (!document) {\n return\n }\n\n eventBus.emit('operation:create:operation', {\n documentName: selectedDocument.value.id,\n path: requestPathTrimmed.value,\n method: selectedMethod.value.method,\n operation: {\n tags: selectedTag.value?.id ? [selectedTag.value.id] : undefined,\n },\n callback: (success) => {\n if (!success) {\n return\n }\n\n /** Build the sidebar */\n workspaceStore.buildSidebar(selectedDocument.value?.id ?? '')\n\n /** Navigate to the example via the event bus rather than the router */\n eventBus.emit('ui:navigate', {\n page: 'example',\n documentSlug: selectedDocument.value?.id,\n path: normalizedRequestPath.value,\n method: selectedMethod.value?.method ?? 'get',\n exampleName: 'default',\n })\n },\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n emit('back', event)\n}\n</script>\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <!-- Request path input -->\n <CommandActionInput\n v-model=\"requestPath\"\n label=\"Request Path\"\n placeholder=\"/users\"\n @delete=\"handleBack\" />\n\n <p\n v-if=\"errorMessage\"\n class=\"text-red px-2 pb-1 text-xs\"\n data-testid=\"command-palette-request-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Selectors for document, method, and tag -->\n <template #options>\n <div class=\"flex flex-1 gap-1\">\n <!-- Document (collection) selector -->\n <ScalarListbox\n v-model=\"selectedDocument\"\n :options=\"availableDocuments\">\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-[150px] min-w-[150px] justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <span :class=\"selectedDocument ? 'text-c-1 truncate' : 'text-c-3'\">\n {{\n selectedDocument ? selectedDocument.label : 'Select Document'\n }}\n </span>\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n\n <!-- HTTP method selector (GET, POST, PUT, etc.) -->\n <ScalarDropdown\n placement=\"bottom\"\n resize>\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-[100px] min-w-[100px] justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <div class=\"flex items-center gap-2\">\n <HttpMethodBadge\n v-if=\"selectedMethod\"\n :method=\"selectedMethod.method\" />\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </div>\n </ScalarButton>\n\n <!-- Dropdown list of all HTTP methods -->\n <template #items>\n <div class=\"custom-scroll max-h-40\">\n <ScalarDropdownItem\n v-for=\"method in availableMethods\"\n :key=\"method.id\"\n class=\"flex h-7 w-full items-center justify-center px-1\"\n @click=\"handleSelectMethod(method)\">\n <HttpMethodBadge :method=\"method.method\" />\n </ScalarDropdownItem>\n </div>\n </template>\n </ScalarDropdown>\n\n <!-- Tag selector (optional) for organizing operations -->\n <ScalarDropdown\n placement=\"bottom\"\n resize>\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-full justify-between gap-1 p-2 text-xs\"\n :disabled=\"!availableTags.length\"\n variant=\"outlined\">\n <span :class=\"selectedTag ? 'text-c-1 truncate' : 'text-c-3'\">\n {{ selectedTag ? selectedTag.label : 'Select Tag (Optional)' }}\n </span>\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n\n <!-- Dropdown list of available tags -->\n <template #items>\n <div class=\"custom-scroll max-h-40\">\n <ScalarDropdownItem\n v-for=\"tag in availableTags\"\n :key=\"tag.id\"\n class=\"flex h-7 w-full items-center px-1\"\n @click=\"handleSelectTag(tag)\">\n <span class=\"truncate\">{{ tag.label }}</span>\n </ScalarDropdownItem>\n </div>\n </template>\n </ScalarDropdown>\n </div>\n </template>\n\n <template #submit>Create Request</template>\n </CommandActionForm>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;CAoBE,MAAM;;;;;;;;;EAoCR,MAAM,OAAO;;EAoBb,MAAM,cAAc,IAAI,IAAG;EAC3B,MAAM,qBAAqB,eAAe,YAAY,MAAM,MAAM,CAAA;;EAGlE,MAAM,wBAAwB,eAC5B,mBAAmB,MAAM,WAAW,IAAG,GACnC,mBAAmB,QACnB,IAAI,mBAAmB,QAC7B;;EAGA,MAAM,qBAAqB,eACzB,OAAO,QAAQ,QAAA,eAAe,UAAU,UAAU,CAAC,KAChD,CAAC,MAAM,eAAe;GACrB,IAAI;GACJ,OAAO,SAAS,KAAK,SAAS;GAC/B,EACF,CACH;;EAGA,MAAM,mBAAmC,aAAa,KAAK,YAAY;GACrE,IAAI;GACJ,OAAO,OAAO,aAAa;GAC3B;GACD,EAAC;EAEF,MAAM,mBAAmB,IACvB,QAAA,eACI,mBAAmB,MAAM,MAAM,aAAa,SAAS,OAAO,QAAA,aAAY,GACvE,mBAAmB,MAAM,MAAM,KAAA,EACtC;EAEA,MAAM,iBAAiB,IACrB,iBAAiB,MAAM,WAAW,OAAO,WAAW,MAAM,CAC5D;;;;;EAMA,MAAM,gBAAgB,eAA4B;AAChD,OAAI,CAAC,iBAAiB,MACpB,QAAO,EAAC;GAGV,MAAM,WAAW,QAAA,eAAe,UAAU,UAAU,iBAAiB,MAAM;AAC3E,OAAI,CAAC,SACH,QAAO,EAAC;AAGV,UAAO,CACL;IAAE,IAAI;IAAI,OAAO;IAAU,EAC3B,GAAI,SAAS,MAAM,KAAK,SAAS;IAC/B,IAAI,IAAI;IACR,OAAO,IAAI;IACZ,EAAE,IAAI,EAAE,CACX;IACD;EAED,MAAM,cAAc,IAClB,QAAA,QAAQ,cAAc,MAAM,MAAM,QAAQ,IAAI,OAAO,QAAA,MAAM,GAAG,KAAA,EAChE;AAGA,QAAM,wBAAwB;AAC5B,eAAY,QAAQ,cAAc,MAAM,MAAM,QAAQ,IAAI,OAAO,GAAE;IACpE;;;;;;;;;EAUD,MAAM,eAA2C,eAAe;AAC9D,OACE,CAAC,mBAAmB,SACpB,CAAC,iBAAiB,SAClB,CAAC,eAAe,MAEhB,QAAO;GAGT,MAAM,WAAW,QAAA,eAAe,UAAU,UAAU,iBAAiB,MAAM;GAC3E,MAAM,SAAS,eAAe,MAAM;AAEpC,OAAI,UAAU,QAAQ,sBAAsB,SAAS,QACnD,QAAO,KAAK,OAAO,aAAa,CAAC,iBAAiB,sBAAsB,MAAM,uBAAuB,iBAAiB,MAAM,MAAM;AAGpI,UAAO;IACR;;;;;;EAOD,MAAM,aAAa,eAEf,CAAC,mBAAmB,SACpB,CAAC,iBAAiB,SAClB,CAAC,eAAe,SAChB,aAAa,UAAU,KAC3B;;EAGA,MAAM,sBAAsB,WAA2C;AACrE,OAAI,OACF,gBAAe,QAAQ;;;EAK3B,MAAM,mBAAmB,QAAqC;AAC5D,OAAI,IACF,aAAY,QAAQ;;;;;;EAQxB,MAAM,qBAA2B;AAC/B,OAAI,WAAW,SAAS,CAAC,iBAAiB,SAAS,CAAC,eAAe,MACjE;AAKF,OAAI,CAFa,QAAA,eAAe,UAAU,UAAU,iBAAiB,MAAM,IAGzE;AAGF,WAAA,SAAS,KAAK,8BAA8B;IAC1C,cAAc,iBAAiB,MAAM;IACrC,MAAM,mBAAmB;IACzB,QAAQ,eAAe,MAAM;IAC7B,WAAW,EACT,MAAM,YAAY,OAAO,KAAK,CAAC,YAAY,MAAM,GAAG,GAAG,KAAA,GACxD;IACD,WAAW,YAAY;AACrB,SAAI,CAAC,QACH;;AAIF,aAAA,eAAe,aAAa,iBAAiB,OAAO,MAAM,GAAE;;AAG5D,aAAA,SAAS,KAAK,eAAe;MAC3B,MAAM;MACN,cAAc,iBAAiB,OAAO;MACtC,MAAM,sBAAsB;MAC5B,QAAQ,eAAe,OAAO,UAAU;MACxC,aAAa;MACd,CAAA;;IAEJ,CAAA;AAED,QAAK,QAAO;;;EAId,MAAM,cAAc,UAA+B;AACjD,QAAK,QAAQ,MAAK;;;uBAIlB,YA0GoB,2BAAA;IAzGjB,UAAU,WAAA;IACV,UAAQ;;IAiBE,SAAO,cAmFV,CAlFN,mBAkFM,OAlFN,YAkFM;KAhFJ,YAgBgB,MAAA,cAAA,EAAA;kBAfL,iBAAA;oFAAgB,QAAA;MACxB,SAAS,mBAAA;;6BAaK,CAZf,YAYe,MAAA,aAAA,EAAA;OAXb,OAAM;OACN,SAAQ;;8BAKD,CAJP,mBAIO,QAAA,EAJA,OAAK,eAAE,iBAAA,QAAgB,sBAAA,WAAA,EAAA,EAAA,gBAE1B,iBAAA,QAAmB,iBAAA,MAAiB,QAAK,kBAAA,EAAA,EAAA,EAG7C,YAGc,MAAA,WAAA,EAAA;QAFZ,OAAM;QACN,MAAK;QACL,MAAK;;;;;;KAKX,YA6BiB,MAAA,eAAA,EAAA;MA5Bf,WAAU;MACV,QAAA;;MAgBW,OAAK,cASR,CARN,mBAQM,OARN,YAQM,EAAA,UAAA,KAAA,EAPJ,mBAMqB,UAAA,MAAA,WALF,MAAA,iBAAgB,GAA1B,WAAM;2BADf,YAMqB,MAAA,mBAAA,EAAA;QAJlB,KAAK,OAAO;QACb,OAAM;QACL,UAAK,WAAE,mBAAmB,OAAM;;+BACU,CAA3C,YAA2C,oBAAA,EAAzB,QAAQ,OAAO,QAAA,EAAA,MAAA,GAAA,CAAA,SAAA,CAAA,CAAA,CAAA;;;;6BAVxB,CAZf,YAYe,MAAA,aAAA,EAAA;OAXb,OAAM;OACN,SAAQ;;8BASF,CARN,mBAQM,OARN,YAQM,CANI,eAAA,SAAA,WAAA,EADR,YAEoC,oBAAA;;QAAjC,QAAQ,eAAA,MAAe;gEAC1B,YAGc,MAAA,WAAA,EAAA;QAFZ,OAAM;QACN,MAAK;QACL,MAAK;;;;;;KAmBb,YA4BiB,MAAA,eAAA,EAAA;MA3Bf,WAAU;MACV,QAAA;;MAeW,OAAK,cASR,CARN,mBAQM,OARN,YAQM,EAAA,UAAA,KAAA,EAPJ,mBAMqB,UAAA,MAAA,WALL,cAAA,QAAP,QAAG;2BADZ,YAMqB,MAAA,mBAAA,EAAA;QAJlB,KAAK,IAAI;QACV,OAAM;QACL,UAAK,WAAE,gBAAgB,IAAG;;+BACkB,CAA7C,mBAA6C,QAA7C,YAA6C,gBAAnB,IAAI,MAAK,EAAA,EAAA,CAAA,CAAA;;;;6BAV1B,CAXf,YAWe,MAAA,aAAA,EAAA;OAVb,OAAM;OACL,UAAQ,CAAG,cAAA,MAAc;OAC1B,SAAQ;;8BAGD,CAFP,mBAEO,QAAA,EAFA,OAAK,eAAE,YAAA,QAAW,sBAAA,WAAA,EAAA,EAAA,gBACpB,YAAA,QAAc,YAAA,MAAY,QAAK,wBAAA,EAAA,EAAA,EAEpC,YAGc,MAAA,WAAA,EAAA;QAFZ,OAAM;QACN,MAAK;QACL,MAAK;;;;;;;IAmBJ,QAAM,cAAe,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAd,kBAAc,GAAA,CAAA,EAAA,CAAA;2BAjGP,CAJzB,YAIyB,4BAAA;iBAHd,YAAA;8EAAW,QAAA;KACpB,OAAM;KACN,aAAY;KACX,UAAQ;iCAGH,aAAA,SAAA,WAAA,EADR,mBAMI,KANJ,YAMI,gBADC,aAAA,MAAY,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteTag.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteTag.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CommandPaletteTag.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteTag.vue"],"names":[],"mappings":"AAwPA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4CAA4C,CAAA;AAO9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,QAAA,MAAM,YAAY;IAEhB,2DAA2D;oBAC3C,cAAc;IAC9B,iDAAiD;cACvC,iBAAiB;IAC3B,mDAAmD;mBACpC,MAAM;IACrB,8EAA8E;UACxE,YAAY;;;;;IAPlB,2DAA2D;oBAC3C,cAAc;IAC9B,iDAAiD;cACvC,iBAAiB;IAC3B,mDAAmD;mBACpC,MAAM;IACrB,8EAA8E;UACxE,YAAY;;;;kFAuUhB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteTag.vue.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteTag.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Tag Component\n *\n * Provides a form for creating or editing a tag in a document (collection).\n * Tags are used to organize and group related API operations.\n *\n * When `tag` is provided, the component enters edit mode where:\n * - The name input is pre-filled with the current tag name\n * - The collection selector is hidden (tag already belongs to a document)\n * - A cancel button is shown to close the modal without saving\n * - Back navigation is disabled (cannot go back with backspace)\n * - Submitting emits an 'edit' event instead of creating a new tag\n *\n * In create mode, validates that the tag name does not already exist\n * in the selected document to prevent duplicates.\n *\n * @example\n * // Create mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n *\n * // Edit mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * :tag=\"tag\"\n * @close=\"handleClose\"\n * @edit=\"handleEdit\"\n * />\n */\nexport default {\n name: 'CommandPaletteTag',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton, ScalarIcon, ScalarListbox } from '@scalar/components'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { TraversedTag } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, ref } from 'vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus, documentName, tag } = defineProps<{\n /** The workspace store for accessing documents and tags */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting tag creation events */\n eventBus: WorkspaceEventBus\n /** Preselected document id to create the tag in */\n documentName?: string\n /** When provided, the component enters edit mode with this name pre-filled */\n tag?: TraversedTag\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the tag is created or edited successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst isEditMode = computed(() => tag !== undefined)\n\nconst name = ref(tag?.name ?? '')\nconst nameTrimmed = computed(() => name.value.trim())\n\n/** All available documents (collections) in the workspace */\nconst availableDocuments = computed(() =>\n Object.entries(workspaceStore.workspace.documents).map(\n ([name, document]) => ({\n id: name,\n label: document.info.title || name,\n }),\n ),\n)\n\nconst selectedDocument = ref<{ id: string; label: string } | undefined>(\n documentName\n ? availableDocuments.value.find((document) => document.id === documentName)\n : (availableDocuments.value[0] ?? undefined),\n)\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"CommandPaletteTag.vue.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteTag.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Tag Component\n *\n * Provides a form for creating or editing a tag in a document (collection).\n * Tags are used to organize and group related API operations.\n *\n * When `tag` is provided, the component enters edit mode where:\n * - The name input is pre-filled with the current tag name\n * - The collection selector is hidden (tag already belongs to a document)\n * - A cancel button is shown to close the modal without saving\n * - Back navigation is disabled (cannot go back with backspace)\n * - Submitting emits an 'edit' event instead of creating a new tag\n *\n * In create mode, validates that the tag name does not already exist\n * in the selected document to prevent duplicates.\n *\n * @example\n * // Create mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n *\n * // Edit mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * :tag=\"tag\"\n * @close=\"handleClose\"\n * @edit=\"handleEdit\"\n * />\n */\nexport default {\n name: 'CommandPaletteTag',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton, ScalarIcon, ScalarListbox } from '@scalar/components'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { TraversedTag } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, ref, type ComputedRef } from 'vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus, documentName, tag } = defineProps<{\n /** The workspace store for accessing documents and tags */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting tag creation events */\n eventBus: WorkspaceEventBus\n /** Preselected document id to create the tag in */\n documentName?: string\n /** When provided, the component enters edit mode with this name pre-filled */\n tag?: TraversedTag\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the tag is created or edited successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst isEditMode = computed(() => tag !== undefined)\n\nconst name = ref(tag?.name ?? '')\nconst nameTrimmed = computed(() => name.value.trim())\n\n/** All available documents (collections) in the workspace */\nconst availableDocuments = computed(() =>\n Object.entries(workspaceStore.workspace.documents).map(\n ([name, document]) => ({\n id: name,\n label: document.info.title || name,\n }),\n ),\n)\n\nconst selectedDocument = ref<{ id: string; label: string } | undefined>(\n documentName\n ? availableDocuments.value.find((document) => document.id === documentName)\n : (availableDocuments.value[0] ?? undefined),\n)\n\n/**\n * Validation message surfaced under the input.\n *\n * Resolves to `null` when the form is valid; otherwise to a human-readable\n * reason the user can act on. Empty input is the default state so we keep\n * the field free of error styling — `isDisabled` still blocks submission\n * there, matching the {@link CommandPaletteOpenApiDocument} pattern.\n *\n * In edit mode, an unchanged name is treated as a no-op (no error shown,\n * but submit stays disabled) so opening the modal does not greet the user\n * with a confusing message about their current name.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (!nameTrimmed.value) {\n return null\n }\n\n const document =\n workspaceStore.workspace.documents[selectedDocument.value?.id ?? '']\n\n if (!selectedDocument.value || !document) {\n return null\n }\n\n // Unchanged name in edit mode is a silent no-op rather than an error\n if (isEditMode.value && nameTrimmed.value === tag?.name) {\n return null\n }\n\n if (document.tags?.some((existing) => existing.name === nameTrimmed.value)) {\n return `A tag named \"${nameTrimmed.value}\" already exists in \"${selectedDocument.value.label}\". Try a different name.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while required fields are missing, the name is unchanged\n * in edit mode, or a duplicate tag exists. The inline `errorMessage` makes\n * the duplicate case explicit instead of leaving the user staring at a\n * silently disabled button.\n */\nconst isDisabled = computed<boolean>(() => {\n const document =\n workspaceStore.workspace.documents[selectedDocument.value?.id ?? '']\n if (!nameTrimmed.value || !selectedDocument.value || !document) {\n return true\n }\n\n if (isEditMode.value && nameTrimmed.value === tag?.name) {\n return true\n }\n\n return errorMessage.value !== null\n})\n\n/**\n * Handle form submission.\n * In edit mode, emits the new name. In create mode, creates the tag via the event bus.\n */\nconst handleSubmit = (): void => {\n if (isDisabled.value || !selectedDocument.value) {\n return\n }\n\n // In edit mode, emit the new name and close\n if (isEditMode.value && tag) {\n eventBus.emit(\n 'tag:edit:tag',\n {\n tag,\n documentName: selectedDocument.value.id,\n newName: nameTrimmed.value,\n },\n { skipUnpackProxy: true },\n )\n emit('close')\n return\n }\n\n eventBus.emit('tag:create:tag', {\n name: nameTrimmed.value,\n documentName: selectedDocument.value.id,\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n // Do not allow back navigation in edit mode\n if (isEditMode.value) {\n return\n }\n emit('back', event)\n}\n\n/** Handle cancel action in edit mode */\nconst handleCancel = (): void => {\n emit('close')\n}\n</script>\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <!-- Tag name input -->\n <CommandActionInput\n v-model=\"name\"\n label=\"Tag Name\"\n placeholder=\"Tag Name\"\n @delete=\"handleBack\" />\n\n <p\n v-if=\"errorMessage\"\n class=\"text-red px-2 pb-1 text-xs\"\n data-testid=\"command-palette-tag-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Collection selector (hidden in edit mode) -->\n <template #options>\n <ScalarListbox\n v-if=\"!isEditMode\"\n v-model=\"selectedDocument\"\n :options=\"availableDocuments\">\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-fit justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <span :class=\"selectedDocument ? 'text-c-1' : 'text-c-3'\">\n {{\n selectedDocument ? selectedDocument.label : 'Select Collection'\n }}\n </span>\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n\n <!-- Cancel button in edit mode -->\n <ScalarButton\n v-if=\"isEditMode\"\n class=\"max-h-8 px-3 text-xs\"\n variant=\"outlined\"\n @click=\"handleCancel\">\n Cancel\n </ScalarButton>\n </template>\n\n <template #submit>{{ isEditMode ? 'Save' : 'Create Tag' }}</template>\n </CommandActionForm>\n</template>\n"],"mappings":""}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import CommandActionForm_default from "./CommandActionForm.vue.js";
|
|
2
2
|
import CommandActionInput_default from "./CommandActionInput.vue.js";
|
|
3
|
-
import { computed, createBlock, createCommentVNode, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
|
|
3
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
|
|
4
4
|
import { ScalarButton, ScalarIcon, ScalarListbox } from "@scalar/components";
|
|
5
|
+
//#region src/v2/features/command-palette/components/CommandPaletteTag.vue?vue&type=script&setup=true&lang.ts
|
|
6
|
+
var _hoisted_1 = {
|
|
7
|
+
key: 0,
|
|
8
|
+
class: "text-red px-2 pb-1 text-xs",
|
|
9
|
+
"data-testid": "command-palette-tag-error",
|
|
10
|
+
role: "alert"
|
|
11
|
+
};
|
|
5
12
|
var CommandPaletteTag_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
6
13
|
name: "CommandPaletteTag",
|
|
7
14
|
props: {
|
|
@@ -23,27 +30,36 @@ var CommandPaletteTag_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
|
|
|
23
30
|
})));
|
|
24
31
|
const selectedDocument = ref(__props.documentName ? availableDocuments.value.find((document) => document.id === __props.documentName) : availableDocuments.value[0] ?? void 0);
|
|
25
32
|
/**
|
|
26
|
-
*
|
|
33
|
+
* Validation message surfaced under the input.
|
|
27
34
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
35
|
+
* Resolves to `null` when the form is valid; otherwise to a human-readable
|
|
36
|
+
* reason the user can act on. Empty input is the default state so we keep
|
|
37
|
+
* the field free of error styling — `isDisabled` still blocks submission
|
|
38
|
+
* there, matching the {@link CommandPaletteOpenApiDocument} pattern.
|
|
32
39
|
*
|
|
33
|
-
* In
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
* In edit mode, an unchanged name is treated as a no-op (no error shown,
|
|
41
|
+
* but submit stays disabled) so opening the modal does not greet the user
|
|
42
|
+
* with a confusing message about their current name.
|
|
43
|
+
*/
|
|
44
|
+
const errorMessage = computed(() => {
|
|
45
|
+
if (!nameTrimmed.value) return null;
|
|
46
|
+
const document = __props.workspaceStore.workspace.documents[selectedDocument.value?.id ?? ""];
|
|
47
|
+
if (!selectedDocument.value || !document) return null;
|
|
48
|
+
if (isEditMode.value && nameTrimmed.value === __props.tag?.name) return null;
|
|
49
|
+
if (document.tags?.some((existing) => existing.name === nameTrimmed.value)) return `A tag named "${nameTrimmed.value}" already exists in "${selectedDocument.value.label}". Try a different name.`;
|
|
50
|
+
return null;
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Submit is blocked while required fields are missing, the name is unchanged
|
|
54
|
+
* in edit mode, or a duplicate tag exists. The inline `errorMessage` makes
|
|
55
|
+
* the duplicate case explicit instead of leaving the user staring at a
|
|
56
|
+
* silently disabled button.
|
|
38
57
|
*/
|
|
39
58
|
const isDisabled = computed(() => {
|
|
40
59
|
const document = __props.workspaceStore.workspace.documents[selectedDocument.value?.id ?? ""];
|
|
41
60
|
if (!nameTrimmed.value || !selectedDocument.value || !document) return true;
|
|
42
|
-
if (isEditMode.value)
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
if (document.tags?.some((tag) => tag.name === nameTrimmed.value)) return true;
|
|
46
|
-
return false;
|
|
61
|
+
if (isEditMode.value && nameTrimmed.value === __props.tag?.name) return true;
|
|
62
|
+
return errorMessage.value !== null;
|
|
47
63
|
});
|
|
48
64
|
/**
|
|
49
65
|
* Handle form submission.
|
|
@@ -114,7 +130,7 @@ var CommandPaletteTag_vue_vue_type_script_setup_true_lang_default = /* @__PURE__
|
|
|
114
130
|
label: "Tag Name",
|
|
115
131
|
placeholder: "Tag Name",
|
|
116
132
|
onDelete: handleBack
|
|
117
|
-
}, null, 8, ["modelValue"])]),
|
|
133
|
+
}, null, 8, ["modelValue"]), errorMessage.value ? (openBlock(), createElementBlock("p", _hoisted_1, toDisplayString(errorMessage.value), 1)) : createCommentVNode("", true)]),
|
|
118
134
|
_: 1
|
|
119
135
|
}, 8, ["disabled"]);
|
|
120
136
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteTag.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteTag.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Tag Component\n *\n * Provides a form for creating or editing a tag in a document (collection).\n * Tags are used to organize and group related API operations.\n *\n * When `tag` is provided, the component enters edit mode where:\n * - The name input is pre-filled with the current tag name\n * - The collection selector is hidden (tag already belongs to a document)\n * - A cancel button is shown to close the modal without saving\n * - Back navigation is disabled (cannot go back with backspace)\n * - Submitting emits an 'edit' event instead of creating a new tag\n *\n * In create mode, validates that the tag name does not already exist\n * in the selected document to prevent duplicates.\n *\n * @example\n * // Create mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n *\n * // Edit mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * :tag=\"tag\"\n * @close=\"handleClose\"\n * @edit=\"handleEdit\"\n * />\n */\nexport default {\n name: 'CommandPaletteTag',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton, ScalarIcon, ScalarListbox } from '@scalar/components'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { TraversedTag } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, ref } from 'vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus, documentName, tag } = defineProps<{\n /** The workspace store for accessing documents and tags */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting tag creation events */\n eventBus: WorkspaceEventBus\n /** Preselected document id to create the tag in */\n documentName?: string\n /** When provided, the component enters edit mode with this name pre-filled */\n tag?: TraversedTag\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the tag is created or edited successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst isEditMode = computed(() => tag !== undefined)\n\nconst name = ref(tag?.name ?? '')\nconst nameTrimmed = computed(() => name.value.trim())\n\n/** All available documents (collections) in the workspace */\nconst availableDocuments = computed(() =>\n Object.entries(workspaceStore.workspace.documents).map(\n ([name, document]) => ({\n id: name,\n label: document.info.title || name,\n }),\n ),\n)\n\nconst selectedDocument = ref<{ id: string; label: string } | undefined>(\n documentName\n ? availableDocuments.value.find((document) => document.id === documentName)\n : (availableDocuments.value[0] ?? undefined),\n)\n\n/**\n *
|
|
1
|
+
{"version":3,"file":"CommandPaletteTag.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteTag.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Tag Component\n *\n * Provides a form for creating or editing a tag in a document (collection).\n * Tags are used to organize and group related API operations.\n *\n * When `tag` is provided, the component enters edit mode where:\n * - The name input is pre-filled with the current tag name\n * - The collection selector is hidden (tag already belongs to a document)\n * - A cancel button is shown to close the modal without saving\n * - Back navigation is disabled (cannot go back with backspace)\n * - Submitting emits an 'edit' event instead of creating a new tag\n *\n * In create mode, validates that the tag name does not already exist\n * in the selected document to prevent duplicates.\n *\n * @example\n * // Create mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n *\n * // Edit mode\n * <CommandPaletteTag\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * :tag=\"tag\"\n * @close=\"handleClose\"\n * @edit=\"handleEdit\"\n * />\n */\nexport default {\n name: 'CommandPaletteTag',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton, ScalarIcon, ScalarListbox } from '@scalar/components'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { TraversedTag } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, ref, type ComputedRef } from 'vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus, documentName, tag } = defineProps<{\n /** The workspace store for accessing documents and tags */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting tag creation events */\n eventBus: WorkspaceEventBus\n /** Preselected document id to create the tag in */\n documentName?: string\n /** When provided, the component enters edit mode with this name pre-filled */\n tag?: TraversedTag\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the tag is created or edited successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst isEditMode = computed(() => tag !== undefined)\n\nconst name = ref(tag?.name ?? '')\nconst nameTrimmed = computed(() => name.value.trim())\n\n/** All available documents (collections) in the workspace */\nconst availableDocuments = computed(() =>\n Object.entries(workspaceStore.workspace.documents).map(\n ([name, document]) => ({\n id: name,\n label: document.info.title || name,\n }),\n ),\n)\n\nconst selectedDocument = ref<{ id: string; label: string } | undefined>(\n documentName\n ? availableDocuments.value.find((document) => document.id === documentName)\n : (availableDocuments.value[0] ?? undefined),\n)\n\n/**\n * Validation message surfaced under the input.\n *\n * Resolves to `null` when the form is valid; otherwise to a human-readable\n * reason the user can act on. Empty input is the default state so we keep\n * the field free of error styling — `isDisabled` still blocks submission\n * there, matching the {@link CommandPaletteOpenApiDocument} pattern.\n *\n * In edit mode, an unchanged name is treated as a no-op (no error shown,\n * but submit stays disabled) so opening the modal does not greet the user\n * with a confusing message about their current name.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (!nameTrimmed.value) {\n return null\n }\n\n const document =\n workspaceStore.workspace.documents[selectedDocument.value?.id ?? '']\n\n if (!selectedDocument.value || !document) {\n return null\n }\n\n // Unchanged name in edit mode is a silent no-op rather than an error\n if (isEditMode.value && nameTrimmed.value === tag?.name) {\n return null\n }\n\n if (document.tags?.some((existing) => existing.name === nameTrimmed.value)) {\n return `A tag named \"${nameTrimmed.value}\" already exists in \"${selectedDocument.value.label}\". Try a different name.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while required fields are missing, the name is unchanged\n * in edit mode, or a duplicate tag exists. The inline `errorMessage` makes\n * the duplicate case explicit instead of leaving the user staring at a\n * silently disabled button.\n */\nconst isDisabled = computed<boolean>(() => {\n const document =\n workspaceStore.workspace.documents[selectedDocument.value?.id ?? '']\n if (!nameTrimmed.value || !selectedDocument.value || !document) {\n return true\n }\n\n if (isEditMode.value && nameTrimmed.value === tag?.name) {\n return true\n }\n\n return errorMessage.value !== null\n})\n\n/**\n * Handle form submission.\n * In edit mode, emits the new name. In create mode, creates the tag via the event bus.\n */\nconst handleSubmit = (): void => {\n if (isDisabled.value || !selectedDocument.value) {\n return\n }\n\n // In edit mode, emit the new name and close\n if (isEditMode.value && tag) {\n eventBus.emit(\n 'tag:edit:tag',\n {\n tag,\n documentName: selectedDocument.value.id,\n newName: nameTrimmed.value,\n },\n { skipUnpackProxy: true },\n )\n emit('close')\n return\n }\n\n eventBus.emit('tag:create:tag', {\n name: nameTrimmed.value,\n documentName: selectedDocument.value.id,\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n // Do not allow back navigation in edit mode\n if (isEditMode.value) {\n return\n }\n emit('back', event)\n}\n\n/** Handle cancel action in edit mode */\nconst handleCancel = (): void => {\n emit('close')\n}\n</script>\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <!-- Tag name input -->\n <CommandActionInput\n v-model=\"name\"\n label=\"Tag Name\"\n placeholder=\"Tag Name\"\n @delete=\"handleBack\" />\n\n <p\n v-if=\"errorMessage\"\n class=\"text-red px-2 pb-1 text-xs\"\n data-testid=\"command-palette-tag-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Collection selector (hidden in edit mode) -->\n <template #options>\n <ScalarListbox\n v-if=\"!isEditMode\"\n v-model=\"selectedDocument\"\n :options=\"availableDocuments\">\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-fit justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <span :class=\"selectedDocument ? 'text-c-1' : 'text-c-3'\">\n {{\n selectedDocument ? selectedDocument.label : 'Select Collection'\n }}\n </span>\n <ScalarIcon\n class=\"text-c-3\"\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n\n <!-- Cancel button in edit mode -->\n <ScalarButton\n v-if=\"isEditMode\"\n class=\"max-h-8 px-3 text-xs\"\n variant=\"outlined\"\n @click=\"handleCancel\">\n Cancel\n </ScalarButton>\n </template>\n\n <template #submit>{{ isEditMode ? 'Save' : 'Create Tag' }}</template>\n </CommandActionForm>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-document-from-source.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/helpers/load-document-from-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;CAC7B,CAAA;AAiCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,GACjC,gBAAgB,cAAc,EAC9B,iBAAiB,eAAe,EAChC,MAAM,MAAM,EACZ,WAAW,OAAO,KACjB,OAAO,CAAC,OAAO,
|
|
1
|
+
{"version":3,"file":"load-document-from-source.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/helpers/load-document-from-source.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAIpE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;CAC7B,CAAA;AAiCD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,GACjC,gBAAgB,cAAc,EAC9B,iBAAiB,eAAe,EAChC,MAAM,MAAM,EACZ,WAAW,OAAO,KACjB,OAAO,CAAC,OAAO,CAwDjB,CAAA"}
|
|
@@ -40,10 +40,11 @@ var readPostmanCollection = () => {
|
|
|
40
40
|
*/
|
|
41
41
|
var loadDocumentFromSource = async (workspaceStore, importEventData, name, watchMode) => {
|
|
42
42
|
const { source, type } = importEventData;
|
|
43
|
-
|
|
43
|
+
const normalizedSource = type === "url" ? source.trim() : source;
|
|
44
|
+
if (!normalizedSource) return false;
|
|
44
45
|
if (type === "url") return await workspaceStore.addDocument({
|
|
45
46
|
name,
|
|
46
|
-
url:
|
|
47
|
+
url: normalizedSource,
|
|
47
48
|
meta: { "x-scalar-watch-mode": watchMode }
|
|
48
49
|
});
|
|
49
50
|
if (type === "file") return await workspaceStore.addDocument({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-document-from-source.js","names":[],"sources":["../../../../../src/v2/features/command-palette/helpers/load-document-from-source.ts"],"sourcesContent":["import { isObject } from '@scalar/helpers/object/is-object'\nimport type { LoaderPlugin } from '@scalar/json-magic/bundle'\nimport { parseJson, parseYaml } from '@scalar/json-magic/bundle/plugins/browser'\nimport { isPostmanCollection } from '@scalar/postman-to-openapi'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport { getOpenApiFromPostman } from '@/v2/features/command-palette/helpers/get-openapi-from-postman'\n\nexport type ImportEventData = {\n source: string\n type: 'url' | 'file' | 'raw'\n}\n\n/**\n * Loader plugin to detect and convert Postman collections into OpenAPI documents\n */\nconst readPostmanCollection = (): LoaderPlugin => {\n return {\n type: 'loader',\n validate: isPostmanCollection,\n exec: (source: string) => {\n try {\n const document = getOpenApiFromPostman(source)\n\n if (document) {\n return Promise.resolve({\n ok: true,\n data: document,\n raw: source,\n })\n }\n\n return Promise.resolve({\n ok: false,\n })\n } catch {\n return Promise.resolve({\n ok: false,\n })\n }\n },\n }\n}\n\n/**\n * Attempts to add a document to the workspace from a given source, which may be a URL or raw content.\n *\n * - If the source is a URL, adds the document by its URL and includes a watch mode flag as metadata.\n * - If the source is a Postman collection, transforms it to OpenAPI and adds it as a document.\n * - For other raw sources (such as pasted JSON or YAML), attempts to normalize and add them as a document.\n *\n * @param workspaceStore The workspace store where the document will be added.\n * @param source The document source (URL, Postman Collection, JSON, or YAML string).\n * @param name The display name for the new document.\n * @param watchMode Whether to enable watch mode (applies only to URL sources).\n * @returns Promise resolving to true if the document was successfully added, or false if the operation failed.\n */\nexport const loadDocumentFromSource = async (\n workspaceStore: WorkspaceStore,\n importEventData: ImportEventData,\n name: string,\n watchMode: boolean,\n): Promise<boolean> => {\n const { source, type } = importEventData\n if (!
|
|
1
|
+
{"version":3,"file":"load-document-from-source.js","names":[],"sources":["../../../../../src/v2/features/command-palette/helpers/load-document-from-source.ts"],"sourcesContent":["import { isObject } from '@scalar/helpers/object/is-object'\nimport type { LoaderPlugin } from '@scalar/json-magic/bundle'\nimport { parseJson, parseYaml } from '@scalar/json-magic/bundle/plugins/browser'\nimport { isPostmanCollection } from '@scalar/postman-to-openapi'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport { getOpenApiFromPostman } from '@/v2/features/command-palette/helpers/get-openapi-from-postman'\n\nexport type ImportEventData = {\n source: string\n type: 'url' | 'file' | 'raw'\n}\n\n/**\n * Loader plugin to detect and convert Postman collections into OpenAPI documents\n */\nconst readPostmanCollection = (): LoaderPlugin => {\n return {\n type: 'loader',\n validate: isPostmanCollection,\n exec: (source: string) => {\n try {\n const document = getOpenApiFromPostman(source)\n\n if (document) {\n return Promise.resolve({\n ok: true,\n data: document,\n raw: source,\n })\n }\n\n return Promise.resolve({\n ok: false,\n })\n } catch {\n return Promise.resolve({\n ok: false,\n })\n }\n },\n }\n}\n\n/**\n * Attempts to add a document to the workspace from a given source, which may be a URL or raw content.\n *\n * - If the source is a URL, adds the document by its URL and includes a watch mode flag as metadata.\n * - If the source is a Postman collection, transforms it to OpenAPI and adds it as a document.\n * - For other raw sources (such as pasted JSON or YAML), attempts to normalize and add them as a document.\n *\n * @param workspaceStore The workspace store where the document will be added.\n * @param source The document source (URL, Postman Collection, JSON, or YAML string).\n * @param name The display name for the new document.\n * @param watchMode Whether to enable watch mode (applies only to URL sources).\n * @returns Promise resolving to true if the document was successfully added, or false if the operation failed.\n */\nexport const loadDocumentFromSource = async (\n workspaceStore: WorkspaceStore,\n importEventData: ImportEventData,\n name: string,\n watchMode: boolean,\n): Promise<boolean> => {\n const { source, type } = importEventData\n const normalizedSource = type === 'url' ? source.trim() : source\n\n if (!normalizedSource) {\n // No source provided, do nothing.\n return false\n }\n\n // If the source is a URL, add it directly with watch mode metadata.\n if (type === 'url') {\n return await workspaceStore.addDocument({\n name,\n url: normalizedSource,\n meta: {\n 'x-scalar-watch-mode': watchMode,\n },\n })\n }\n\n if (type === 'file') {\n return await workspaceStore.addDocument({\n name,\n path: source,\n })\n }\n\n const loaders = [readPostmanCollection(), parseJson(), parseYaml()]\n const loader = loaders.find((l) => l.validate(source))\n\n // If no loader is found, return false\n if (!loader) {\n return false\n }\n\n // Execute the loader\n const result = await loader.exec(source)\n // If the loader failed, return false\n if (!result.ok) {\n return false\n }\n\n if (!isObject(result.data)) {\n return false\n }\n\n const addDocumentResult = await workspaceStore.addDocument({\n name,\n document: result.data,\n })\n\n if (!addDocumentResult) {\n return false\n }\n\n return true\n}\n"],"mappings":";;;;;;;;AAgBA,IAAM,8BAA4C;AAChD,QAAO;EACL,MAAM;EACN,UAAU;EACV,OAAO,WAAmB;AACxB,OAAI;IACF,MAAM,WAAW,sBAAsB,OAAO;AAE9C,QAAI,SACF,QAAO,QAAQ,QAAQ;KACrB,IAAI;KACJ,MAAM;KACN,KAAK;KACN,CAAC;AAGJ,WAAO,QAAQ,QAAQ,EACrB,IAAI,OACL,CAAC;WACI;AACN,WAAO,QAAQ,QAAQ,EACrB,IAAI,OACL,CAAC;;;EAGP;;;;;;;;;;;;;;;AAgBH,IAAa,yBAAyB,OACpC,gBACA,iBACA,MACA,cACqB;CACrB,MAAM,EAAE,QAAQ,SAAS;CACzB,MAAM,mBAAmB,SAAS,QAAQ,OAAO,MAAM,GAAG;AAE1D,KAAI,CAAC,iBAEH,QAAO;AAIT,KAAI,SAAS,MACX,QAAO,MAAM,eAAe,YAAY;EACtC;EACA,KAAK;EACL,MAAM,EACJ,uBAAuB,WACxB;EACF,CAAC;AAGJ,KAAI,SAAS,OACX,QAAO,MAAM,eAAe,YAAY;EACtC;EACA,MAAM;EACP,CAAC;CAIJ,MAAM,SADU;EAAC,uBAAuB;EAAE,WAAW;EAAE,WAAW;EAAC,CAC5C,MAAM,MAAM,EAAE,SAAS,OAAO,CAAC;AAGtD,KAAI,CAAC,OACH,QAAO;CAIT,MAAM,SAAS,MAAM,OAAO,KAAK,OAAO;AAExC,KAAI,CAAC,OAAO,GACV,QAAO;AAGT,KAAI,CAAC,SAAS,OAAO,KAAK,CACxB,QAAO;AAQT,KAAI,CALsB,MAAM,eAAe,YAAY;EACzD;EACA,UAAU,OAAO;EAClB,CAAC,CAGA,QAAO;AAGT,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-url.d.ts","sourceRoot":"","sources":["../../../src/v2/helpers/is-url.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"is-url.d.ts","sourceRoot":"","sources":["../../../src/v2/helpers/is-url.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,YAIlC,CAAA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { isValidUrl } from "@scalar/helpers/url/is-valid-url";
|
|
2
2
|
//#region src/v2/helpers/is-url.ts
|
|
3
3
|
var isUrl = (input) => {
|
|
4
|
-
|
|
4
|
+
const trimmedInput = input.trim();
|
|
5
|
+
return (trimmedInput.startsWith("http://") || trimmedInput.startsWith("https://")) && isValidUrl(trimmedInput);
|
|
5
6
|
};
|
|
6
7
|
//#endregion
|
|
7
8
|
export { isUrl };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-url.js","names":[],"sources":["../../../src/v2/helpers/is-url.ts"],"sourcesContent":["import { isValidUrl } from '@scalar/helpers/url/is-valid-url'\n\nexport const isUrl = (input: string) => {\n return (
|
|
1
|
+
{"version":3,"file":"is-url.js","names":[],"sources":["../../../src/v2/helpers/is-url.ts"],"sourcesContent":["import { isValidUrl } from '@scalar/helpers/url/is-valid-url'\n\nexport const isUrl = (input: string) => {\n const trimmedInput = input.trim()\n\n return (trimmedInput.startsWith('http://') || trimmedInput.startsWith('https://')) && isValidUrl(trimmedInput)\n}\n"],"mappings":";;AAEA,IAAa,SAAS,UAAkB;CACtC,MAAM,eAAe,MAAM,MAAM;AAEjC,SAAQ,aAAa,WAAW,UAAU,IAAI,aAAa,WAAW,WAAW,KAAK,WAAW,aAAa"}
|
package/dist/vue-styles.css
CHANGED
|
@@ -773,10 +773,10 @@ to {
|
|
|
773
773
|
* `LoadingSkeleton.vue` easing/duration used in `@scalar/api-reference` so
|
|
774
774
|
* any skeleton in the app feels consistent.
|
|
775
775
|
*/
|
|
776
|
-
.sidebar-skeleton-row > span[data-v-
|
|
777
|
-
animation: sidebar-skeleton-pulse-
|
|
776
|
+
.sidebar-skeleton-row > span[data-v-e5f6f69d] {
|
|
777
|
+
animation: sidebar-skeleton-pulse-e5f6f69d 1.5s infinite alternate;
|
|
778
778
|
}
|
|
779
|
-
@keyframes sidebar-skeleton-pulse-
|
|
779
|
+
@keyframes sidebar-skeleton-pulse-e5f6f69d {
|
|
780
780
|
from {
|
|
781
781
|
opacity: 1;
|
|
782
782
|
}
|
|
@@ -891,7 +891,7 @@ to {
|
|
|
891
891
|
* currently *selected* version so the user can spot it at a glance, even
|
|
892
892
|
* after moving the cursor or arrowing to another row.
|
|
893
893
|
*/
|
|
894
|
-
.version-picker[data-v-
|
|
894
|
+
.version-picker[data-v-28138913] [role='option'][aria-selected='true'] {
|
|
895
895
|
background-color: var(--scalar-background-2);
|
|
896
896
|
}
|
|
897
897
|
|
|
@@ -1265,7 +1265,7 @@ to {
|
|
|
1265
1265
|
* Custom horizontal scroll for the URL preview.
|
|
1266
1266
|
* Hides scrollbar for a cleaner appearance while maintaining scroll functionality.
|
|
1267
1267
|
*/
|
|
1268
|
-
.scroll-timeline-x[data-v-
|
|
1268
|
+
.scroll-timeline-x[data-v-311ac78f] {
|
|
1269
1269
|
overflow: auto;
|
|
1270
1270
|
scroll-timeline: --scroll-timeline x;
|
|
1271
1271
|
/* Firefox support */
|
|
@@ -1277,7 +1277,7 @@ to {
|
|
|
1277
1277
|
}
|
|
1278
1278
|
|
|
1279
1279
|
/* Hide scrollbar in Chrome, Safari, and Opera */
|
|
1280
|
-
.scroll-timeline-x[data-v-
|
|
1280
|
+
.scroll-timeline-x[data-v-311ac78f]::-webkit-scrollbar {
|
|
1281
1281
|
display: none;
|
|
1282
1282
|
}
|
|
1283
1283
|
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"rest",
|
|
19
19
|
"testing"
|
|
20
20
|
],
|
|
21
|
-
"version": "3.6.
|
|
21
|
+
"version": "3.6.1",
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=22"
|
|
24
24
|
},
|
|
@@ -338,19 +338,19 @@
|
|
|
338
338
|
"@scalar/components": "0.24.2",
|
|
339
339
|
"@scalar/helpers": "0.6.0",
|
|
340
340
|
"@scalar/icons": "0.7.2",
|
|
341
|
-
"@scalar/oas-utils": "0.15.2",
|
|
342
341
|
"@scalar/json-magic": "0.12.12",
|
|
342
|
+
"@scalar/oas-utils": "0.15.3",
|
|
343
343
|
"@scalar/openapi-types": "0.8.0",
|
|
344
|
-
"@scalar/
|
|
344
|
+
"@scalar/sidebar": "0.9.11",
|
|
345
345
|
"@scalar/snippetz": "0.9.6",
|
|
346
|
+
"@scalar/postman-to-openapi": "0.7.5",
|
|
346
347
|
"@scalar/themes": "0.15.3",
|
|
347
|
-
"@scalar/types": "0.9.6",
|
|
348
348
|
"@scalar/use-codemirror": "0.14.11",
|
|
349
|
+
"@scalar/types": "0.9.6",
|
|
350
|
+
"@scalar/use-hooks": "0.4.3",
|
|
349
351
|
"@scalar/validation": "0.3.2",
|
|
350
352
|
"@scalar/use-toasts": "0.10.2",
|
|
351
|
-
"@scalar/
|
|
352
|
-
"@scalar/sidebar": "0.9.10",
|
|
353
|
-
"@scalar/workspace-store": "0.49.2"
|
|
353
|
+
"@scalar/workspace-store": "0.49.3"
|
|
354
354
|
},
|
|
355
355
|
"devDependencies": {
|
|
356
356
|
"@tailwindcss/cli": "^4.2.4",
|
|
@@ -367,7 +367,7 @@
|
|
|
367
367
|
"vite-svg-loader": "5.1.1",
|
|
368
368
|
"vitest": "4.1.0",
|
|
369
369
|
"@scalar/sdk": "0.1.5",
|
|
370
|
-
"@scalar/pre-post-request-scripts": "0.4.
|
|
370
|
+
"@scalar/pre-post-request-scripts": "0.4.12"
|
|
371
371
|
},
|
|
372
372
|
"scripts": {
|
|
373
373
|
"build": "vite build && pnpm build:styles && vue-tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|