@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
|
@@ -2,14 +2,19 @@ import HttpMethod_default from "../../../blocks/operation-code-sample/components
|
|
|
2
2
|
import CommandActionForm_default from "./CommandActionForm.vue.js";
|
|
3
3
|
import CommandActionInput_default from "./CommandActionInput.vue.js";
|
|
4
4
|
import { getOperationFromCurl } from "../helpers/get-operation-from-curl.js";
|
|
5
|
-
import { computed, createBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
|
|
5
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
|
|
6
6
|
import { ScalarButton, ScalarIcon, ScalarListbox } from "@scalar/components";
|
|
7
|
-
import { useRouter } from "vue-router";
|
|
8
7
|
//#region src/v2/features/command-palette/components/CommandPaletteImportCurl.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
-
var _hoisted_1 = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
var _hoisted_1 = {
|
|
9
|
+
key: 0,
|
|
10
|
+
class: "text-red px-2 pb-1 text-xs",
|
|
11
|
+
"data-testid": "command-palette-import-curl-error",
|
|
12
|
+
role: "alert"
|
|
13
|
+
};
|
|
14
|
+
var _hoisted_2 = { class: "flex flex-1 flex-col gap-2" };
|
|
15
|
+
var _hoisted_3 = { class: "flex h-9 flex-row items-center gap-2 rounded border p-[3px] text-sm" };
|
|
16
|
+
var _hoisted_4 = { class: "scroll-timeline-x whitespace-nowrap" };
|
|
17
|
+
var _hoisted_5 = { class: "flex items-center gap-2" };
|
|
13
18
|
var CommandPaletteImportCurl_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
14
19
|
name: "CommandPaletteImportCurl",
|
|
15
20
|
props: {
|
|
@@ -20,7 +25,6 @@ var CommandPaletteImportCurl_vue_vue_type_script_setup_true_lang_default = /* @_
|
|
|
20
25
|
emits: ["close", "back"],
|
|
21
26
|
setup(__props, { emit: __emit }) {
|
|
22
27
|
const emit = __emit;
|
|
23
|
-
const router = useRouter();
|
|
24
28
|
const exampleKey = ref("");
|
|
25
29
|
/** Trimmed version of the example key for validation and submission */
|
|
26
30
|
const exampleKeyTrimmed = computed(() => exampleKey.value.trim());
|
|
@@ -33,18 +37,25 @@ var CommandPaletteImportCurl_vue_vue_type_script_setup_true_lang_default = /* @_
|
|
|
33
37
|
})));
|
|
34
38
|
const selectedDocument = ref(documents.value[0]);
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
40
|
+
* Validation message surfaced under the input.
|
|
41
|
+
*
|
|
42
|
+
* Resolves to `null` when the form is valid; otherwise to a human-readable
|
|
43
|
+
* reason the user can act on. Empty input is the default state so we keep
|
|
44
|
+
* the field free of error styling — `isDisabled` still blocks submission
|
|
45
|
+
* there, matching the {@link CommandPaletteOpenApiDocument} pattern.
|
|
41
46
|
*/
|
|
42
|
-
const
|
|
43
|
-
if (!exampleKeyTrimmed.value || !selectedDocument.value) return
|
|
44
|
-
if (__props.workspaceStore.workspace.documents[selectedDocument.value.id]?.paths?.[path]?.[method]) return
|
|
45
|
-
return
|
|
47
|
+
const errorMessage = computed(() => {
|
|
48
|
+
if (!exampleKeyTrimmed.value || !selectedDocument.value) return null;
|
|
49
|
+
if (__props.workspaceStore.workspace.documents[selectedDocument.value.id]?.paths?.[path]?.[method]) return `A ${method.toUpperCase()} operation at "${path}" already exists in "${selectedDocument.value.label}". Importing this cURL would conflict with it.`;
|
|
50
|
+
return null;
|
|
46
51
|
});
|
|
47
52
|
/**
|
|
53
|
+
* Submit is blocked while required fields are missing or a duplicate exists.
|
|
54
|
+
* The inline `errorMessage` makes the duplicate case explicit so the user
|
|
55
|
+
* understands why import is unavailable rather than seeing a silent disable.
|
|
56
|
+
*/
|
|
57
|
+
const isDisabled = computed(() => !exampleKeyTrimmed.value || !selectedDocument.value || errorMessage.value !== null);
|
|
58
|
+
/**
|
|
48
59
|
* Handle the import submission.
|
|
49
60
|
* Creates a new operation in the selected document from the parsed cURL command.
|
|
50
61
|
*/
|
|
@@ -60,19 +71,16 @@ var CommandPaletteImportCurl_vue_vue_type_script_setup_true_lang_default = /* @_
|
|
|
60
71
|
operation: result.operation,
|
|
61
72
|
exampleKey: exampleKeyTrimmed.value,
|
|
62
73
|
callback: (success) => {
|
|
63
|
-
if (success)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}
|
|
74
|
+
if (!success) return;
|
|
75
|
+
__props.workspaceStore.buildSidebar(documentName.id);
|
|
76
|
+
const normalizedPath = result.path.startsWith("/") ? result.path : `/${result.path}`;
|
|
77
|
+
__props.eventBus.emit("ui:navigate", {
|
|
78
|
+
page: "example",
|
|
79
|
+
documentSlug: documentName.id,
|
|
80
|
+
path: normalizedPath,
|
|
81
|
+
method: result.method,
|
|
82
|
+
exampleName: exampleKeyTrimmed.value
|
|
83
|
+
});
|
|
76
84
|
}
|
|
77
85
|
});
|
|
78
86
|
emit("close");
|
|
@@ -86,7 +94,7 @@ var CommandPaletteImportCurl_vue_vue_type_script_setup_true_lang_default = /* @_
|
|
|
86
94
|
disabled: isDisabled.value,
|
|
87
95
|
onSubmit: handleImportClick
|
|
88
96
|
}, {
|
|
89
|
-
options: withCtx(() => [createElementVNode("div",
|
|
97
|
+
options: withCtx(() => [createElementVNode("div", _hoisted_5, [createVNode(unref(ScalarListbox), {
|
|
90
98
|
modelValue: selectedDocument.value,
|
|
91
99
|
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedDocument.value = $event),
|
|
92
100
|
options: documents.value
|
|
@@ -105,15 +113,19 @@ var CommandPaletteImportCurl_vue_vue_type_script_setup_true_lang_default = /* @_
|
|
|
105
113
|
_: 1
|
|
106
114
|
}, 8, ["modelValue", "options"])])]),
|
|
107
115
|
submit: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode("Import Request", -1)])]),
|
|
108
|
-
default: withCtx(() => [
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
default: withCtx(() => [
|
|
117
|
+
createVNode(CommandActionInput_default, {
|
|
118
|
+
modelValue: exampleKey.value,
|
|
119
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => exampleKey.value = $event),
|
|
120
|
+
placeholder: "Curl example key (e.g., example-1)",
|
|
121
|
+
onDelete: handleBack
|
|
122
|
+
}, null, 8, ["modelValue"]),
|
|
123
|
+
errorMessage.value ? (openBlock(), createElementBlock("p", _hoisted_1, toDisplayString(errorMessage.value), 1)) : createCommentVNode("", true),
|
|
124
|
+
createElementVNode("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [createVNode(HttpMethod_default, {
|
|
125
|
+
class: "border-r-1 px-1",
|
|
126
|
+
method: unref(method)
|
|
127
|
+
}, null, 8, ["method"]), createElementVNode("span", _hoisted_4, toDisplayString(unref(operation).servers?.[0]?.url || "") + toDisplayString(unref(path)), 1)])])
|
|
128
|
+
]),
|
|
117
129
|
_: 1
|
|
118
130
|
}, 8, ["disabled"]);
|
|
119
131
|
};
|
package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteImportCurl.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteImportCurl.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Import cURL Component\n *\n * Provides a form for importing API requests from cURL commands.\n * Parses the cURL command to extract the HTTP method, URL, path, headers,\n * and body, then creates a new operation in the selected document.\n *\n * Validates that no conflicting operation exists at the same path/method.\n *\n * @example\n * <CommandPaletteImportCurl\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * :curl=\"curlCommand\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {\n name: 'CommandPaletteImportCurl',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIcon,\n ScalarListbox,\n type ScalarComboboxOption,\n} from '@scalar/components'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref
|
|
1
|
+
{"version":3,"file":"CommandPaletteImportCurl.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteImportCurl.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette Import cURL Component\n *\n * Provides a form for importing API requests from cURL commands.\n * Parses the cURL command to extract the HTTP method, URL, path, headers,\n * and body, then creates a new operation in the selected document.\n *\n * Validates that no conflicting operation exists at the same path/method.\n *\n * @example\n * <CommandPaletteImportCurl\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * :curl=\"curlCommand\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {\n name: 'CommandPaletteImportCurl',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIcon,\n ScalarListbox,\n type ScalarComboboxOption,\n} from '@scalar/components'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\n\nimport HttpMethod from '@/v2/blocks/operation-code-sample/components/HttpMethod.vue'\nimport CommandActionForm from '@/v2/features/command-palette/components/CommandActionForm.vue'\nimport CommandActionInput from '@/v2/features/command-palette/components/CommandActionInput.vue'\nimport { getOperationFromCurl } from '@/v2/features/command-palette/helpers/get-operation-from-curl'\n\nconst { workspaceStore, inputValue, eventBus } = 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 /** The cURL command string to parse and import */\n inputValue: string\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the import is complete */\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 exampleKey = ref('')\n\n/** Trimmed version of the example key for validation and submission */\nconst exampleKeyTrimmed = computed<string>(() => exampleKey.value.trim())\n\n/** Parse the cURL command to extract path, method, and operation details */\nconst { path, method, operation } = getOperationFromCurl(inputValue)\n\n/** List of all available documents (collections) in the workspace */\nconst documents = computed(() =>\n Object.keys(workspaceStore.workspace.documents).map((document) => ({\n id: document,\n label: document,\n })),\n)\n\nconst selectedDocument = ref<ScalarComboboxOption | undefined>(\n documents.value[0],\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 (!exampleKeyTrimmed.value || !selectedDocument.value) {\n return null\n }\n\n const document = workspaceStore.workspace.documents[selectedDocument.value.id]\n\n if (document?.paths?.[path]?.[method]) {\n return `A ${method.toUpperCase()} operation at \"${path}\" already exists in \"${selectedDocument.value.label}\". Importing this cURL would conflict with it.`\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` makes the duplicate case explicit so the user\n * understands why import is unavailable rather than seeing a silent disable.\n */\nconst isDisabled = computed<boolean>(\n () =>\n !exampleKeyTrimmed.value ||\n !selectedDocument.value ||\n errorMessage.value !== null,\n)\n\n/**\n * Handle the import submission.\n * Creates a new operation in the selected document from the parsed cURL command.\n */\nconst handleImportClick = (): void => {\n const documentName = selectedDocument.value\n\n if (isDisabled.value || !documentName) {\n return\n }\n\n /** Re-parse with the example key to include it in the operation */\n const result = getOperationFromCurl(inputValue, exampleKeyTrimmed.value)\n\n eventBus.emit('operation:create:operation', {\n documentName: documentName.id,\n path: result.path,\n method: result.method,\n operation: result.operation,\n exampleKey: exampleKeyTrimmed.value,\n callback: (success) => {\n if (!success) {\n return\n }\n\n // build the sidebar\n workspaceStore.buildSidebar(documentName.id)\n\n const normalizedPath = result.path.startsWith('/')\n ? result.path\n : `/${result.path}`\n\n // Navigate to the new example via the event bus rather than the router\n eventBus.emit('ui:navigate', {\n page: 'example',\n documentSlug: documentName.id,\n path: normalizedPath,\n method: result.method,\n exampleName: exampleKeyTrimmed.value,\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=\"handleImportClick\">\n <!-- Example key input -->\n <CommandActionInput\n v-model=\"exampleKey\"\n placeholder=\"Curl example key (e.g., example-1)\"\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-import-curl-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Preview of the parsed cURL request (method + URL + path) -->\n <div class=\"flex flex-1 flex-col gap-2\">\n <div\n class=\"flex h-9 flex-row items-center gap-2 rounded border p-[3px] text-sm\">\n <HttpMethod\n class=\"border-r-1 px-1\"\n :method=\"method\" />\n <span class=\"scroll-timeline-x whitespace-nowrap\">\n {{ operation.servers?.[0]?.url || '' }}{{ path }}\n </span>\n </div>\n </div>\n\n <!-- Document selector -->\n <template #options>\n <div class=\"flex items-center gap-2\">\n <ScalarListbox\n v-model=\"selectedDocument\"\n :options=\"documents\">\n <ScalarButton\n class=\"hover:bg-b-2 max-h-8 w-full justify-between gap-1 p-2 text-xs\"\n variant=\"outlined\">\n <span\n class=\"whitespace-nowrap\"\n :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 </div>\n </template>\n\n <template #submit>Import Request</template>\n </CommandActionForm>\n</template>\n<style scoped>\n/**\n * Custom horizontal scroll for the URL preview.\n * Hides scrollbar for a cleaner appearance while maintaining scroll functionality.\n */\n.scroll-timeline-x {\n overflow: auto;\n scroll-timeline: --scroll-timeline x;\n /* Firefox support */\n scroll-timeline: --scroll-timeline horizontal;\n /* Hide scrollbar in IE and Edge */\n -ms-overflow-style: none;\n /* Hide scrollbar in Firefox */\n scrollbar-width: none;\n}\n\n/* Hide scrollbar in Chrome, Safari, and Opera */\n.scroll-timeline-x::-webkit-scrollbar {\n display: none;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;CAoBE,MAAM;;;;;;;;EA6BR,MAAM,OAAO;EAOb,MAAM,aAAa,IAAI,GAAE;;EAGzB,MAAM,oBAAoB,eAAuB,WAAW,MAAM,MAAM,CAAA;;EAGxE,MAAM,EAAE,MAAM,QAAQ,cAAc,qBAAqB,QAAA,WAAU;;EAGnE,MAAM,YAAY,eAChB,OAAO,KAAK,QAAA,eAAe,UAAU,UAAU,CAAC,KAAK,cAAc;GACjE,IAAI;GACJ,OAAO;GACR,EAAE,CACL;EAEA,MAAM,mBAAmB,IACvB,UAAU,MAAM,GAClB;;;;;;;;;EAUA,MAAM,eAA2C,eAAe;AAC9D,OAAI,CAAC,kBAAkB,SAAS,CAAC,iBAAiB,MAChD,QAAO;AAKT,OAFiB,QAAA,eAAe,UAAU,UAAU,iBAAiB,MAAM,KAE7D,QAAQ,QAAQ,QAC5B,QAAO,KAAK,OAAO,aAAa,CAAC,iBAAiB,KAAK,uBAAuB,iBAAiB,MAAM,MAAM;AAG7G,UAAO;IACR;;;;;;EAOD,MAAM,aAAa,eAEf,CAAC,kBAAkB,SACnB,CAAC,iBAAiB,SAClB,aAAa,UAAU,KAC3B;;;;;EAMA,MAAM,0BAAgC;GACpC,MAAM,eAAe,iBAAiB;AAEtC,OAAI,WAAW,SAAS,CAAC,aACvB;;GAIF,MAAM,SAAS,qBAAqB,QAAA,YAAY,kBAAkB,MAAK;AAEvE,WAAA,SAAS,KAAK,8BAA8B;IAC1C,cAAc,aAAa;IAC3B,MAAM,OAAO;IACb,QAAQ,OAAO;IACf,WAAW,OAAO;IAClB,YAAY,kBAAkB;IAC9B,WAAW,YAAY;AACrB,SAAI,CAAC,QACH;AAIF,aAAA,eAAe,aAAa,aAAa,GAAE;KAE3C,MAAM,iBAAiB,OAAO,KAAK,WAAW,IAAG,GAC7C,OAAO,OACP,IAAI,OAAO;AAGf,aAAA,SAAS,KAAK,eAAe;MAC3B,MAAM;MACN,cAAc,aAAa;MAC3B,MAAM;MACN,QAAQ,OAAO;MACf,aAAa,kBAAkB;MAChC,CAAA;;IAEJ,CAAA;AAED,QAAK,QAAO;;;EAId,MAAM,cAAc,UAA+B;AACjD,QAAK,QAAQ,MAAK;;;uBAIlB,YAwDoB,2BAAA;IAvDjB,UAAU,WAAA;IACV,UAAQ;;IA6BE,SAAO,cAqBV,CApBN,mBAoBM,OApBN,YAoBM,CAnBJ,YAkBgB,MAAA,cAAA,EAAA;iBAjBL,iBAAA;mFAAgB,QAAA;KACxB,SAAS,UAAA;;4BAeK,CAdf,YAce,MAAA,aAAA,EAAA;MAbb,OAAM;MACN,SAAQ;;6BAOD,CANP,mBAMO,QAAA,EALL,OAAK,eAAA,CAAC,qBACE,iBAAA,QAAgB,aAAA,WAAA,CAAA,EAAA,EAAA,gBAEtB,iBAAA,QAAmB,iBAAA,MAAiB,QAAK,oBAAA,EAAA,EAAA,EAG7C,YAGc,MAAA,WAAA,EAAA;OAFZ,OAAM;OACN,MAAK;OACL,MAAK;;;;;;IAMJ,QAAM,cAAe,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAd,kBAAc,GAAA,CAAA,EAAA,CAAA;2BAhDP;KAHzB,YAGyB,4BAAA;kBAFd,WAAA;8EAAU,QAAA;MACnB,aAAY;MACX,UAAQ;;KAGH,aAAA,SAAA,WAAA,EADR,mBAMI,KANJ,YAMI,gBADC,aAAA,MAAY,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA;KAIjB,mBAUM,OAVN,YAUM,CATJ,mBAQM,OARN,YAQM,CANJ,YAEqB,oBAAA;MADnB,OAAM;MACL,QAAQ,MAAA,OAAM;8BACjB,mBAEO,QAFP,YAEO,gBADF,MAAA,UAAS,CAAC,UAAO,IAAO,OAAG,GAAA,GAAA,gBAAY,MAAA,KAAI,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA"}
|
package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteOpenApiDocument.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CommandPaletteOpenApiDocument.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"names":[],"mappings":"AA2JA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AASvE;;;;;;;;;;;;;;GAcG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,2DAA2D;oBAC3C,cAAc;IAC9B,sDAAsD;cAC5C,iBAAiB;;;;;IAH3B,2DAA2D;oBAC3C,cAAc;IAC9B,sDAAsD;cAC5C,iBAAiB;;;;kFAiOzB,CAAC"}
|
package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteOpenApiDocument.vue.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\
|
|
1
|
+
{"version":3,"file":"CommandPaletteOpenApiDocument.vue.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\n\nimport IconSelector from '@/components/IconSelector.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus } = defineProps<{\n /** The workspace store for accessing existing documents */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting document creation events */\n eventBus: WorkspaceEventBus\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the document 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\nconst documentName = ref('')\nconst documentNameTrimmed = computed(() => documentName.value.trim())\n\n/** Default icon for new documents (folder icon) */\nconst documentIcon = ref('interface-content-folder')\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 `CreateVersionModal` pattern.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (!documentNameTrimmed.value) {\n return null\n }\n\n if (\n workspaceStore.workspace.documents[documentNameTrimmed.value] !== undefined\n ) {\n return `A document named \"${documentNameTrimmed.value}\" already exists. Try a different name.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while the input is empty or fails validation. We keep the\n * button disabled (rather than letting the user fire a no-op submit) because\n * the inline `errorMessage` already explains the fix out loud.\n */\nconst isDisabled = computed<boolean>(\n () => !documentNameTrimmed.value || errorMessage.value !== null,\n)\n\n/** Handle form submission to create a new document */\nconst handleSubmit = (): void => {\n if (isDisabled.value) {\n return\n }\n\n eventBus.emit('document:create:empty-document', {\n name: documentNameTrimmed.value,\n icon: documentIcon.value,\n callback: (success) => {\n if (!success) {\n return\n }\n\n // Navigate via the event bus rather than the router\n eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'overview',\n documentSlug: documentNameTrimmed.value,\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\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <CommandActionInput\n v-model=\"documentName\"\n label=\"Document Name\"\n placeholder=\"Document 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-document-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Icon selector for choosing document icon -->\n <template #options>\n <IconSelector\n v-model=\"documentIcon\"\n placement=\"bottom-start\">\n <ScalarButton\n class=\"aspect-square h-auto px-0\"\n variant=\"outlined\">\n <LibraryIcon\n class=\"text-c-2 size-4 stroke-[1.75]\"\n :src=\"documentIcon\" />\n </ScalarButton>\n </IconSelector>\n </template>\n\n <template #submit>Create Document</template>\n </CommandActionForm>\n</template>\n"],"mappings":""}
|
package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js
CHANGED
|
@@ -3,7 +3,6 @@ import CommandActionInput_default from "./CommandActionInput.vue.js";
|
|
|
3
3
|
import IconSelector_default from "../../../../components/IconSelector.vue.js";
|
|
4
4
|
import { computed, createBlock, createCommentVNode, createElementBlock, createTextVNode, createVNode, defineComponent, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
|
|
5
5
|
import { ScalarButton } from "@scalar/components";
|
|
6
|
-
import { useRouter } from "vue-router";
|
|
7
6
|
import { LibraryIcon } from "@scalar/icons/library";
|
|
8
7
|
//#region src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue?vue&type=script&setup=true&lang.ts
|
|
9
8
|
var _hoisted_1 = {
|
|
@@ -21,7 +20,6 @@ var CommandPaletteOpenApiDocument_vue_vue_type_script_setup_true_lang_default =
|
|
|
21
20
|
emits: ["close", "back"],
|
|
22
21
|
setup(__props, { emit: __emit }) {
|
|
23
22
|
const emit = __emit;
|
|
24
|
-
const router = useRouter();
|
|
25
23
|
const documentName = ref("");
|
|
26
24
|
const documentNameTrimmed = computed(() => documentName.value.trim());
|
|
27
25
|
/** Default icon for new documents (folder icon) */
|
|
@@ -52,9 +50,11 @@ var CommandPaletteOpenApiDocument_vue_vue_type_script_setup_true_lang_default =
|
|
|
52
50
|
name: documentNameTrimmed.value,
|
|
53
51
|
icon: documentIcon.value,
|
|
54
52
|
callback: (success) => {
|
|
55
|
-
if (success)
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
if (!success) return;
|
|
54
|
+
__props.eventBus.emit("ui:navigate", {
|
|
55
|
+
page: "document",
|
|
56
|
+
path: "overview",
|
|
57
|
+
documentSlug: documentNameTrimmed.value
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
});
|
package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteOpenApiDocument.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\
|
|
1
|
+
{"version":3,"file":"CommandPaletteOpenApiDocument.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\n\nimport IconSelector from '@/components/IconSelector.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus } = defineProps<{\n /** The workspace store for accessing existing documents */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting document creation events */\n eventBus: WorkspaceEventBus\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the document 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\nconst documentName = ref('')\nconst documentNameTrimmed = computed(() => documentName.value.trim())\n\n/** Default icon for new documents (folder icon) */\nconst documentIcon = ref('interface-content-folder')\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 `CreateVersionModal` pattern.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (!documentNameTrimmed.value) {\n return null\n }\n\n if (\n workspaceStore.workspace.documents[documentNameTrimmed.value] !== undefined\n ) {\n return `A document named \"${documentNameTrimmed.value}\" already exists. Try a different name.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while the input is empty or fails validation. We keep the\n * button disabled (rather than letting the user fire a no-op submit) because\n * the inline `errorMessage` already explains the fix out loud.\n */\nconst isDisabled = computed<boolean>(\n () => !documentNameTrimmed.value || errorMessage.value !== null,\n)\n\n/** Handle form submission to create a new document */\nconst handleSubmit = (): void => {\n if (isDisabled.value) {\n return\n }\n\n eventBus.emit('document:create:empty-document', {\n name: documentNameTrimmed.value,\n icon: documentIcon.value,\n callback: (success) => {\n if (!success) {\n return\n }\n\n // Navigate via the event bus rather than the router\n eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'overview',\n documentSlug: documentNameTrimmed.value,\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\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <CommandActionInput\n v-model=\"documentName\"\n label=\"Document Name\"\n placeholder=\"Document 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-document-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Icon selector for choosing document icon -->\n <template #options>\n <IconSelector\n v-model=\"documentIcon\"\n placement=\"bottom-start\">\n <ScalarButton\n class=\"aspect-square h-auto px-0\"\n variant=\"outlined\">\n <LibraryIcon\n class=\"text-c-2 size-4 stroke-[1.75]\"\n :src=\"documentIcon\" />\n </ScalarButton>\n </IconSelector>\n </template>\n\n <template #submit>Create Document</template>\n </CommandActionForm>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAsCA,MAAM,OAAO;EAOb,MAAM,eAAe,IAAI,GAAE;EAC3B,MAAM,sBAAsB,eAAe,aAAa,MAAM,MAAM,CAAA;;EAGpE,MAAM,eAAe,IAAI,2BAA0B;;;;;;;;;EAUnD,MAAM,eAA2C,eAAe;AAC9D,OAAI,CAAC,oBAAoB,MACvB,QAAO;AAGT,OACE,QAAA,eAAe,UAAU,UAAU,oBAAoB,WAAW,KAAA,EAElE,QAAO,qBAAqB,oBAAoB,MAAM;AAGxD,UAAO;IACR;;;;;;EAOD,MAAM,aAAa,eACX,CAAC,oBAAoB,SAAS,aAAa,UAAU,KAC7D;;EAGA,MAAM,qBAA2B;AAC/B,OAAI,WAAW,MACb;AAGF,WAAA,SAAS,KAAK,kCAAkC;IAC9C,MAAM,oBAAoB;IAC1B,MAAM,aAAa;IACnB,WAAW,YAAY;AACrB,SAAI,CAAC,QACH;AAIF,aAAA,SAAS,KAAK,eAAe;MAC3B,MAAM;MACN,MAAM;MACN,cAAc,oBAAoB;MACnC,CAAA;;IAEJ,CAAA;AAED,QAAK,QAAO;;;EAId,MAAM,cAAc,UAA+B;AACjD,QAAK,QAAQ,MAAK;;;uBAKlB,YAiCoB,2BAAA;IAhCjB,UAAU,WAAA;IACV,UAAQ;;IAgBE,SAAO,cAWD,CAVf,YAUe,sBAAA;iBATJ,aAAA;+EAAY,QAAA;KACrB,WAAU;;4BAOK,CANf,YAMe,MAAA,aAAA,EAAA;MALb,OAAM;MACN,SAAQ;;6BAGgB,CAFxB,YAEwB,MAAA,YAAA,EAAA;OADtB,OAAM;OACL,KAAK,aAAA;;;;;;IAKH,QAAM,cAAgB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAf,mBAAe,GAAA,CAAA,EAAA,CAAA;2BAzBR,CAJzB,YAIyB,4BAAA;iBAHd,aAAA;+EAAY,QAAA;KACrB,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":"CommandPaletteRequest.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteRequest.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CommandPaletteRequest.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteRequest.vue"],"names":[],"mappings":"AAmXA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AASvE;;;;;;;;;;;;;;;;;GAiBG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAKzC,QAAA,MAAM,YAAY;IAEhB,iEAAiE;oBACjD,cAAc;IAC9B,uDAAuD;cAC7C,iBAAiB;IAC3B,uDAAuD;mBACxC,MAAM;IACrB,0DAA0D;YAClD,MAAM;;;;;IAPd,iEAAiE;oBACjD,cAAc;IAC9B,uDAAuD;cAC7C,iBAAiB;IAC3B,uDAAuD;mBACxC,MAAM;IACrB,0DAA0D;YAClD,MAAM;;;;kFAgmBZ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CommandPaletteRequest.vue.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":""}
|
|
1
|
+
{"version":3,"file":"CommandPaletteRequest.vue.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":""}
|
|
@@ -3,14 +3,19 @@ import CommandActionForm_default from "./CommandActionForm.vue.js";
|
|
|
3
3
|
import CommandActionInput_default from "./CommandActionInput.vue.js";
|
|
4
4
|
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, renderList, toDisplayString, unref, watch, withCtx } from "vue";
|
|
5
5
|
import { ScalarButton, ScalarDropdown, ScalarDropdownItem, ScalarIcon, ScalarListbox } from "@scalar/components";
|
|
6
|
-
import { useRouter } from "vue-router";
|
|
7
6
|
import { HTTP_METHODS } from "@scalar/helpers/http/http-methods";
|
|
8
7
|
//#region src/v2/features/command-palette/components/CommandPaletteRequest.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
-
var _hoisted_1 = {
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
var _hoisted_1 = {
|
|
9
|
+
key: 0,
|
|
10
|
+
class: "text-red px-2 pb-1 text-xs",
|
|
11
|
+
"data-testid": "command-palette-request-error",
|
|
12
|
+
role: "alert"
|
|
13
|
+
};
|
|
14
|
+
var _hoisted_2 = { class: "flex flex-1 gap-1" };
|
|
15
|
+
var _hoisted_3 = { class: "flex items-center gap-2" };
|
|
12
16
|
var _hoisted_4 = { class: "custom-scroll max-h-40" };
|
|
13
|
-
var _hoisted_5 = { class: "
|
|
17
|
+
var _hoisted_5 = { class: "custom-scroll max-h-40" };
|
|
18
|
+
var _hoisted_6 = { class: "truncate" };
|
|
14
19
|
var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
15
20
|
name: "CommandPaletteRequest",
|
|
16
21
|
props: {
|
|
@@ -23,9 +28,10 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
23
28
|
setup(__props, { emit: __emit }) {
|
|
24
29
|
const emit = __emit;
|
|
25
30
|
/** HTTP method option type for selectors */
|
|
26
|
-
const router = useRouter();
|
|
27
31
|
const requestPath = ref("/");
|
|
28
32
|
const requestPathTrimmed = computed(() => requestPath.value.trim());
|
|
33
|
+
/** Ensure path starts with '/' for consistent lookup */
|
|
34
|
+
const normalizedRequestPath = computed(() => requestPathTrimmed.value.startsWith("/") ? requestPathTrimmed.value : `/${requestPathTrimmed.value}`);
|
|
29
35
|
/** All available documents (collections) in the workspace */
|
|
30
36
|
const availableDocuments = computed(() => Object.entries(__props.workspaceStore.workspace.documents).map(([name, document]) => ({
|
|
31
37
|
id: name,
|
|
@@ -60,26 +66,26 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
60
66
|
selectedTag.value = availableTags.value.find((tag) => tag.id === "");
|
|
61
67
|
});
|
|
62
68
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
69
|
+
* Validation message surfaced under the input.
|
|
70
|
+
*
|
|
71
|
+
* Resolves to `null` when the form is valid; otherwise to a human-readable
|
|
72
|
+
* reason the user can act on. Empty input is the default state so we keep
|
|
73
|
+
* the field free of error styling — `isDisabled` still blocks submission
|
|
74
|
+
* there, matching the {@link CommandPaletteOpenApiDocument} pattern.
|
|
65
75
|
*/
|
|
66
|
-
const
|
|
67
|
-
if (!
|
|
76
|
+
const errorMessage = computed(() => {
|
|
77
|
+
if (!requestPathTrimmed.value || !selectedDocument.value || !selectedMethod.value) return null;
|
|
68
78
|
const document = __props.workspaceStore.workspace.documents[selectedDocument.value.id];
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return
|
|
79
|
+
const method = selectedMethod.value.method;
|
|
80
|
+
if (document?.paths?.[normalizedRequestPath.value]?.[method]) return `A ${method.toUpperCase()} operation at "${normalizedRequestPath.value}" already exists in "${selectedDocument.value.label}". Try a different path or method.`;
|
|
81
|
+
return null;
|
|
72
82
|
});
|
|
73
83
|
/**
|
|
74
|
-
*
|
|
75
|
-
*
|
|
84
|
+
* Submit is blocked while required fields are missing or a duplicate exists.
|
|
85
|
+
* The inline `errorMessage` explains the duplicate case so the user knows how
|
|
86
|
+
* to recover instead of facing a silently disabled button.
|
|
76
87
|
*/
|
|
77
|
-
const isDisabled = computed(() =>
|
|
78
|
-
if (!requestPathTrimmed.value || !selectedDocument.value || !selectedMethod.value) return true;
|
|
79
|
-
/** Prevent creating duplicate operations */
|
|
80
|
-
if (operationExists.value) return true;
|
|
81
|
-
return false;
|
|
82
|
-
});
|
|
88
|
+
const isDisabled = computed(() => !requestPathTrimmed.value || !selectedDocument.value || !selectedMethod.value || errorMessage.value !== null);
|
|
83
89
|
/** Handle HTTP method selection from dropdown */
|
|
84
90
|
const handleSelectMethod = (method) => {
|
|
85
91
|
if (method) selectedMethod.value = method;
|
|
@@ -101,21 +107,17 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
101
107
|
method: selectedMethod.value.method,
|
|
102
108
|
operation: { tags: selectedTag.value?.id ? [selectedTag.value.id] : void 0 },
|
|
103
109
|
callback: (success) => {
|
|
104
|
-
if (success)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
exampleName: "default"
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
110
|
+
if (!success) return;
|
|
111
|
+
/** Build the sidebar */
|
|
112
|
+
__props.workspaceStore.buildSidebar(selectedDocument.value?.id ?? "");
|
|
113
|
+
/** Navigate to the example via the event bus rather than the router */
|
|
114
|
+
__props.eventBus.emit("ui:navigate", {
|
|
115
|
+
page: "example",
|
|
116
|
+
documentSlug: selectedDocument.value?.id,
|
|
117
|
+
path: normalizedRequestPath.value,
|
|
118
|
+
method: selectedMethod.value?.method ?? "get",
|
|
119
|
+
exampleName: "default"
|
|
120
|
+
});
|
|
119
121
|
}
|
|
120
122
|
});
|
|
121
123
|
emit("close");
|
|
@@ -129,7 +131,7 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
129
131
|
disabled: isDisabled.value,
|
|
130
132
|
onSubmit: handleSubmit
|
|
131
133
|
}, {
|
|
132
|
-
options: withCtx(() => [createElementVNode("div",
|
|
134
|
+
options: withCtx(() => [createElementVNode("div", _hoisted_2, [
|
|
133
135
|
createVNode(unref(ScalarListbox), {
|
|
134
136
|
modelValue: selectedDocument.value,
|
|
135
137
|
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedDocument.value = $event),
|
|
@@ -152,7 +154,7 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
152
154
|
placement: "bottom",
|
|
153
155
|
resize: ""
|
|
154
156
|
}, {
|
|
155
|
-
items: withCtx(() => [createElementVNode("div",
|
|
157
|
+
items: withCtx(() => [createElementVNode("div", _hoisted_4, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(availableMethods), (method) => {
|
|
156
158
|
return openBlock(), createBlock(unref(ScalarDropdownItem), {
|
|
157
159
|
key: method.id,
|
|
158
160
|
class: "flex h-7 w-full items-center justify-center px-1",
|
|
@@ -166,7 +168,7 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
166
168
|
class: "hover:bg-b-2 max-h-8 w-[100px] min-w-[100px] justify-between gap-1 p-2 text-xs",
|
|
167
169
|
variant: "outlined"
|
|
168
170
|
}, {
|
|
169
|
-
default: withCtx(() => [createElementVNode("div",
|
|
171
|
+
default: withCtx(() => [createElementVNode("div", _hoisted_3, [selectedMethod.value ? (openBlock(), createBlock(HttpMethod_default, {
|
|
170
172
|
key: 0,
|
|
171
173
|
method: selectedMethod.value.method
|
|
172
174
|
}, null, 8, ["method"])) : createCommentVNode("", true), createVNode(unref(ScalarIcon), {
|
|
@@ -182,13 +184,13 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
182
184
|
placement: "bottom",
|
|
183
185
|
resize: ""
|
|
184
186
|
}, {
|
|
185
|
-
items: withCtx(() => [createElementVNode("div",
|
|
187
|
+
items: withCtx(() => [createElementVNode("div", _hoisted_5, [(openBlock(true), createElementBlock(Fragment, null, renderList(availableTags.value, (tag) => {
|
|
186
188
|
return openBlock(), createBlock(unref(ScalarDropdownItem), {
|
|
187
189
|
key: tag.id,
|
|
188
190
|
class: "flex h-7 w-full items-center px-1",
|
|
189
191
|
onClick: ($event) => handleSelectTag(tag)
|
|
190
192
|
}, {
|
|
191
|
-
default: withCtx(() => [createElementVNode("span",
|
|
193
|
+
default: withCtx(() => [createElementVNode("span", _hoisted_6, toDisplayString(tag.label), 1)]),
|
|
192
194
|
_: 2
|
|
193
195
|
}, 1032, ["onClick"]);
|
|
194
196
|
}), 128))])]),
|
|
@@ -214,7 +216,7 @@ var CommandPaletteRequest_vue_vue_type_script_setup_true_lang_default = /* @__PU
|
|
|
214
216
|
label: "Request Path",
|
|
215
217
|
placeholder: "/users",
|
|
216
218
|
onDelete: handleBack
|
|
217
|
-
}, null, 8, ["modelValue"])]),
|
|
219
|
+
}, null, 8, ["modelValue"]), errorMessage.value ? (openBlock(), createElementBlock("p", _hoisted_1, toDisplayString(errorMessage.value), 1)) : createCommentVNode("", true)]),
|
|
218
220
|
_: 1
|
|
219
221
|
}, 8, ["disabled"]);
|
|
220
222
|
};
|