@scalar/api-client 3.21.0 → 3.21.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/style.css +22 -16
  3. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
  4. package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
  5. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +4 -0
  6. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
  7. package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts +2 -0
  8. package/dist/v2/blocks/request-block/RequestBlock.vue.d.ts.map +1 -1
  9. package/dist/v2/blocks/request-block/RequestBlock.vue.js +1 -1
  10. package/dist/v2/blocks/request-block/RequestBlock.vue.js.map +1 -1
  11. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js +7 -0
  12. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
  13. package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts +4 -0
  14. package/dist/v2/blocks/request-block/components/RequestBody.vue.d.ts.map +1 -1
  15. package/dist/v2/blocks/request-block/components/RequestBody.vue.js +1 -1
  16. package/dist/v2/blocks/request-block/components/RequestBody.vue.js.map +1 -1
  17. package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js +8 -5
  18. package/dist/v2/blocks/request-block/components/RequestBody.vue.script.js.map +1 -1
  19. package/dist/v2/blocks/request-block/components/RequestBodyForm.vue.d.ts +2 -0
  20. package/dist/v2/blocks/request-block/components/RequestBodyForm.vue.d.ts.map +1 -1
  21. package/dist/v2/blocks/request-block/components/RequestBodyForm.vue.js.map +1 -1
  22. package/dist/v2/blocks/request-block/components/RequestBodyForm.vue.script.js +12 -4
  23. package/dist/v2/blocks/request-block/components/RequestBodyForm.vue.script.js.map +1 -1
  24. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.d.ts.map +1 -1
  25. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.js.map +1 -1
  26. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js +3 -0
  27. package/dist/v2/blocks/request-block/components/RequestCodeSnippet.vue.script.js.map +1 -1
  28. package/dist/v2/blocks/request-block/components/RequestTable.vue.d.ts.map +1 -1
  29. package/dist/v2/blocks/request-block/components/RequestTable.vue.js +1 -1
  30. package/dist/v2/blocks/request-block/components/RequestTable.vue.js.map +1 -1
  31. package/dist/v2/blocks/request-block/components/RequestTable.vue.script.js +1 -0
  32. package/dist/v2/blocks/request-block/components/RequestTable.vue.script.js.map +1 -1
  33. package/dist/v2/blocks/request-block/components/RequestTableRow.vue.js.map +1 -1
  34. package/dist/v2/blocks/request-block/components/RequestTableRow.vue.script.js +2 -2
  35. package/dist/v2/blocks/request-block/components/RequestTableRow.vue.script.js.map +1 -1
  36. package/dist/v2/blocks/request-block/helpers/get-form-body-rows.d.ts.map +1 -1
  37. package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js +5 -3
  38. package/dist/v2/blocks/request-block/helpers/get-form-body-rows.js.map +1 -1
  39. package/dist/v2/blocks/response-block/ResponseBlock.vue.d.ts.map +1 -1
  40. package/dist/v2/blocks/response-block/ResponseBlock.vue.js +1 -1
  41. package/dist/v2/blocks/response-block/ResponseBlock.vue.js.map +1 -1
  42. package/dist/v2/blocks/response-block/ResponseBlock.vue.script.js +4 -1
  43. package/dist/v2/blocks/response-block/ResponseBlock.vue.script.js.map +1 -1
  44. package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
  45. package/dist/v2/constants.js +1 -1
  46. package/dist/vue-styles.css +16 -16
  47. package/package.json +11 -11
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBody.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarCodeBlockCopy } from '@scalar/components/code-block'\nimport { ScalarCopyBackdrop } from '@scalar/components/copy'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { ScalarIconMagicWand } from '@scalar/icons'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport {\n getExampleValue,\n getExplicitExampleText,\n} from '@scalar/workspace-store/helpers/get-example-value'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSchemaExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n title,\n defaultView = 'raw',\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /**\n * Initial view for structured (JSON/YAML) bodies. Comes from the\n * `x-scalar-default-request-body-view` document extension and falls back to `raw`\n * whenever the body cannot be shown as a form.\n */\n defaultView?: 'form' | 'raw'\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n /**\n * Create a new example seeded with values generated from the request body schema.\n *\n * The block itself owns the document and operation identity needed to create an example, so we\n * only report the content type the user is currently looking at.\n */\n (e: 'generate:example', payload: { contentType: string }): void\n}>()\n\nconst { translate } = useLocalization()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/** Keep the editor and copy label aligned with the selected content type. */\nconst selectedLanguage = computed(\n () =>\n contentTypeToLanguageMap[\n selectedContentType.value as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext',\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\n/** MIME values remain stable while their human-readable labels follow the locale. */\nconst localizedContentTypes = computed(() => ({\n ...CONTENT_TYPES,\n 'multipart/form-data': translate('apiClient.requestBody.multipartForm'),\n 'application/x-www-form-urlencoded': translate(\n 'apiClient.requestBody.formUrlEncoded',\n ),\n 'application/octet-stream': translate('apiClient.requestBody.binaryFile'),\n 'other': translate('apiClient.requestBody.other'),\n 'none': translate('apiClient.requestBody.none'),\n}))\n\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(localizedContentTypes.value).map(\n ([id, label]) => ({\n id,\n label,\n }),\n )\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n localizedContentTypes.value[essence as keyof typeof CONTENT_TYPES] ??\n essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const selected = getExampleValue(example.value)\n const explicitText = getExplicitExampleText(\n selected,\n selectedContentType.value,\n 2,\n )\n if (explicitText !== undefined) {\n return explicitText\n }\n const value = selected?.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema ??\n requestBody?.content?.[selectedContentType.value]?.itemSchema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const selected = getExampleValue(example.value ?? undefined)\n // Structured data is already parsed; parsing strings again changes the payload type.\n if (example.value?.dataValue !== undefined) {\n return { ok: true, value: example.value.dataValue }\n }\n const raw = selected?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/**\n * An edited example normally takes precedence over schema-generated data. When a different\n * composition branch is selected, however, keeping that example leaves the editor showing the\n * previous branch. Track the selection itself instead of inspecting discriminator fields so this\n * also works for compositions whose members are distinguished only by their shape.\n */\nconst compositionSelectionKey = computed(() =>\n JSON.stringify(requestBodyCompositionSelection ?? {}),\n)\n\n// Switching operations changes the body (and often the selection) at once, which is not a branch\n// change, so we only regenerate the body when the selection changes while the operation and example\n// stay the same. Watching all three together and comparing against the watcher's own previous values\n// keeps this correct regardless of the order in which props update or watchers flush.\nwatch(\n [() => requestBody, () => exampleKey, compositionSelectionKey],\n (\n [, , selection],\n [previousRequestBody, previousExampleKey, previousSelection],\n ) => {\n const operationChanged =\n requestBody !== previousRequestBody || exampleKey !== previousExampleKey\n const selectionChanged = selection !== previousSelection\n\n // Going from no selection to a populated one is the modal applying the reference's selection as\n // the panel opens, not the user switching branches. Resetting here would discard the body's named\n // example on the very first open (issue #10075), so only a change between two populated selections\n // counts as a real branch switch.\n const hadNoPreviousSelection = previousSelection === '{}'\n\n // Only a genuine branch switch within the same operation should reset the edited body. An empty\n // selection means there is no composition to switch between (or the selection was cleared on an\n // operation change), so there is nothing to reset.\n if (\n operationChanged ||\n !selectionChanged ||\n hadNoPreviousSelection ||\n Object.keys(requestBodyCompositionSelection ?? {}).length === 0\n ) {\n return\n }\n\n const codec = structuredCodec.value\n if (!requestBody || !codec) {\n return\n }\n\n // Regenerate through the same helper the initial example uses so a reset produces exactly the\n // value a fresh render of this branch would, rather than a hand-copied second code path.\n const selectedValue = getSchemaExampleFromBody(\n requestBody,\n selectedContentType.value,\n requestBodyCompositionSelection,\n )\n\n emits('update:value', {\n // A branch with no writable content generates `null`/`undefined`; clear the editor rather than\n // writing the literal text `null` or leaving the previously selected branch's body behind.\n payload: selectedValue == null ? '' : codec.stringify(selectedValue),\n contentType: selectedContentType.value,\n })\n },\n)\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, seeded from the document default (raw unless configured) */\nconst bodyView = ref<'form' | 'raw'>(defaultView)\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n\n/**\n * Whether we can offer to generate a fresh example from the schema.\n *\n * Only structured (JSON/YAML) bodies qualify: they have a schema to generate from and a text\n * representation we can seed the new example with. Binary and form bodies have their own editors,\n * so the button would have nothing meaningful to write.\n */\nconst canGenerateExample = computed(() =>\n Boolean(structuredCodec.value && bodySchema.value),\n)\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <div class=\"flex items-center gap-1\">\n <!--\n Generates a new example from the schema so the auto-generated fields stay reachable\n even once the document ships custom examples.\n -->\n <ScalarIconButton\n v-if=\"canGenerateExample\"\n aria-label=\"Generate example from schema\"\n :icon=\"ScalarIconMagicWand\"\n label=\"Generate example from schema\"\n size=\"sm\"\n tooltip\n @click.stop=\"\n emits('generate:example', { contentType: selectedContentType })\n \" />\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </div>\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>{{ translate('apiClient.requestBody.noBody') }}</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n {{ translate('apiClient.requestBody.delete') }}\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>{{ translate('apiClient.requestBody.selectFile') }}</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <!-- Editable raw body with a copy button revealed on hover/focus -->\n <div\n v-else\n class=\"group/code-block relative border-t\">\n <CodeInput\n class=\"px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"selectedLanguage\"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <ScalarCodeBlockCopy\n v-if=\"bodyValue\"\n class=\"absolute top-1.5 right-1.5\"\n :content=\"bodyValue\"\n :lang=\"selectedLanguage\"\n showLang>\n <template #backdrop>\n <ScalarCopyBackdrop class=\"-top-1 -right-1.5\" />\n </template>\n </ScalarCodeBlockCopy>\n </div>\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RequestBody.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBody.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarCodeBlockCopy } from '@scalar/components/code-block'\nimport { ScalarCopyBackdrop } from '@scalar/components/copy'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarListbox } from '@scalar/components/listbox'\nimport { CONTENT_TYPES } from '@scalar/helpers/http/content-types'\nimport { isXmlMediaType } from '@scalar/helpers/http/is-xml-media-type'\nimport { parseMimeType } from '@scalar/helpers/http/mime-type'\nimport { isObject } from '@scalar/helpers/object/is-object'\nimport { objectEntries } from '@scalar/helpers/object/object-entries'\nimport { ScalarIconMagicWand } from '@scalar/icons'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport {\n getExampleValue,\n getExplicitExampleText,\n} from '@scalar/workspace-store/helpers/get-example-value'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport {\n getExampleFromBody,\n getSchemaExampleFromBody,\n getSelectedBodyContentType,\n} from '@scalar/workspace-store/request-example'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n RequestBodyObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { isObjectSchema } from '@scalar/workspace-store/schemas/v3.2/strict/type-guards'\nimport { computed, ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestBodyForm from '@/v2/blocks/request-block/components/RequestBodyForm.vue'\nimport RequestBodyStructured from '@/v2/blocks/request-block/components/RequestBodyStructured.vue'\nimport RequestBodyViewToggle from '@/v2/blocks/request-block/components/RequestBodyViewToggle.vue'\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { getStructuredBodyCodec } from '@/v2/blocks/request-block/helpers/structured-body-codec'\nimport { CodeInput } from '@/v2/components/code-input'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n requestBody,\n exampleKey,\n environment,\n requestBodyCompositionSelection,\n openapiVersion,\n title,\n defaultView = 'raw',\n} = defineProps<{\n /** Request body */\n requestBody?: RequestBodyObject\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Display title */\n title: string\n /** Selected environment */\n environment: XScalarEnvironment\n /** Originating OpenAPI version, used for XML mapping rules. */\n openapiVersion?: string\n /** Selected anyOf/oneOf request-body variants keyed by schema path */\n requestBodyCompositionSelection?: Record<string, number>\n /**\n * Initial view for structured (JSON/YAML) bodies. Comes from the\n * `x-scalar-default-request-body-view` document extension and falls back to `raw`\n * whenever the body cannot be shown as a form.\n */\n defaultView?: 'form' | 'raw'\n}>()\n\nconst emits = defineEmits<{\n (e: 'update:contentType', payload: { value: string }): void\n /** We use this event to update raw values */\n (\n e: 'update:value',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:value'],\n 'payload' | 'contentType'\n >,\n ): void\n /** We use this event when updating form data only */\n (\n e: 'update:formValue',\n payload: Pick<\n ApiReferenceEvents['operation:update:requestBody:formValue'],\n 'payload' | 'contentType'\n >,\n ): void\n /**\n * Create a new example seeded with values generated from the request body schema.\n *\n * The block itself owns the document and operation identity needed to create an example, so we\n * only report the content type the user is currently looking at.\n */\n (e: 'generate:example', payload: { contentType: string }): void\n}>()\n\nconst { translate } = useLocalization()\n\n// Map a content type to a language for the code editor\nconst contentTypeToLanguageMap = {\n 'application/json': 'json',\n 'application/xml': 'xml',\n 'application/yaml': 'yaml',\n} as const\n\n/** Selected content type with default */\nconst selectedContentType = computed(\n () => getSelectedBodyContentType(requestBody, exampleKey) ?? 'none',\n)\n\n/** Keep the editor and copy label aligned with the selected content type. */\nconst selectedLanguage = computed(() =>\n isXmlMediaType(selectedContentType.value)\n ? 'xml'\n : (contentTypeToLanguageMap[\n selectedContentType.value as keyof typeof contentTypeToLanguageMap\n ] ?? 'plaintext'),\n)\n\n/**\n * Strips MIME parameters (such as `charset=utf-8`) for dropdown labels, aligned with how we treat\n * media types elsewhere (see api-reference `normalizeMimeType` / WHATWG essence).\n */\nconst contentTypeLabel = (raw: string): string => parseMimeType(raw).essence\n\n/**\n * Build the dropdown options for the content type selector.\n *\n * The list is composed of two groups:\n * 1. The built-in content types we ship friendly labels for (JSON, XML, Multipart Form, etc.).\n * 2. Any additional content types defined on the OpenAPI request body that are not in the built-in list\n * (for example `text/csv`, `application/pdf`, vendor types like `application/vnd.api+json`, ...).\n *\n * The OpenAPI-defined extras are appended at the bottom so the well-known options stay on top, and\n * users can always pick the exact content type the operation actually accepts.\n */\n/** MIME values remain stable while their human-readable labels follow the locale. */\nconst localizedContentTypes = computed(() => ({\n ...CONTENT_TYPES,\n 'multipart/form-data': translate('apiClient.requestBody.multipartForm'),\n 'application/x-www-form-urlencoded': translate(\n 'apiClient.requestBody.formUrlEncoded',\n ),\n 'application/octet-stream': translate('apiClient.requestBody.binaryFile'),\n 'other': translate('apiClient.requestBody.other'),\n 'none': translate('apiClient.requestBody.none'),\n}))\n\nconst contentTypeOptions = computed<{ id: string; label: string }[]>(() => {\n const builtIn = objectEntries(localizedContentTypes.value).map(\n ([id, label]) => ({\n id,\n label,\n }),\n )\n\n const extras = Object.keys(requestBody?.content ?? {})\n .filter((type) => {\n if (type in CONTENT_TYPES) {\n return false\n }\n // Same essence as a built-in (e.g. `application/json; charset=utf-8`) — do not duplicate the row.\n return !(contentTypeLabel(type) in CONTENT_TYPES)\n })\n .map((type) => ({ id: type, label: contentTypeLabel(type) }))\n\n return [...builtIn, ...extras]\n})\n\nconst selectedContentTypeModel = computed<{ id: string; label: string }>({\n get: () => {\n const found = contentTypeOptions.value.find(\n (it) => it.id === selectedContentType.value,\n )\n if (found) {\n return found\n }\n\n const essence = contentTypeLabel(selectedContentType.value)\n const friendly =\n localizedContentTypes.value[essence as keyof typeof CONTENT_TYPES] ??\n essence\n\n return {\n id: selectedContentType.value,\n label: friendly,\n }\n },\n set: (v) => {\n emits('update:contentType', { value: v.id })\n },\n})\n\nfunction handleFileUpload(callback: (file: File) => void) {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n if (file) {\n callback(file)\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n\n/** Dereferenced example */\nconst example = computed(\n () =>\n requestBody &&\n getExampleFromBody(\n requestBody,\n selectedContentType.value,\n exampleKey,\n requestBodyCompositionSelection,\n openapiVersion,\n ),\n)\n\n/** Convert the example value to a string for the code editor */\nconst bodyValue = computed(() => {\n if (!example.value) {\n return ''\n }\n\n const selected = getExampleValue(example.value)\n const explicitText = getExplicitExampleText(\n selected,\n selectedContentType.value,\n 2,\n )\n if (explicitText !== undefined) {\n return explicitText\n }\n const value = selected?.value\n if (typeof value === 'string') {\n return value\n }\n\n return JSON.stringify(value, null, 2)\n})\n\n/** Resolved schema for the request body */\nconst bodySchema = computed<SchemaObject | undefined>(() => {\n return resolve.schema(\n requestBody?.content?.[selectedContentType.value]?.schema ??\n requestBody?.content?.[selectedContentType.value]?.itemSchema,\n )\n})\n\n/** Codec for structured (JSON/YAML) bodies, undefined for everything else */\nconst structuredCodec = computed(() =>\n selectedContentType.value === 'none'\n ? undefined\n : getStructuredBodyCodec(selectedContentType.value),\n)\n\n/** Parsed body value for the form view — `ok: false` when the raw text is not parseable */\nconst parsedBody = computed<{ ok: boolean; value?: unknown }>(() => {\n const codec = structuredCodec.value\n if (!codec) {\n return { ok: false }\n }\n\n const selected = getExampleValue(example.value ?? undefined)\n // Structured data is already parsed; parsing strings again changes the payload type.\n if (example.value?.dataValue !== undefined) {\n return { ok: true, value: example.value.dataValue }\n }\n const raw = selected?.value\n // An empty body is still form-editable: rows come from the schema.\n if (raw === undefined || raw === null || raw === '') {\n return { ok: true, value: {} }\n }\n if (typeof raw === 'string') {\n try {\n return { ok: true, value: codec.parse(raw) }\n } catch {\n return { ok: false }\n }\n }\n // Spec-provided examples can be stored as objects directly.\n return { ok: true, value: raw }\n})\n\n/**\n * An edited example normally takes precedence over schema-generated data. When a different\n * composition branch is selected, however, keeping that example leaves the editor showing the\n * previous branch. Track the selection itself instead of inspecting discriminator fields so this\n * also works for compositions whose members are distinguished only by their shape.\n */\nconst compositionSelectionKey = computed(() =>\n JSON.stringify(requestBodyCompositionSelection ?? {}),\n)\n\n// Switching operations changes the body (and often the selection) at once, which is not a branch\n// change, so we only regenerate the body when the selection changes while the operation and example\n// stay the same. Watching all three together and comparing against the watcher's own previous values\n// keeps this correct regardless of the order in which props update or watchers flush.\nwatch(\n [() => requestBody, () => exampleKey, compositionSelectionKey],\n (\n [, , selection],\n [previousRequestBody, previousExampleKey, previousSelection],\n ) => {\n const operationChanged =\n requestBody !== previousRequestBody || exampleKey !== previousExampleKey\n const selectionChanged = selection !== previousSelection\n\n // Going from no selection to a populated one is the modal applying the reference's selection as\n // the panel opens, not the user switching branches. Resetting here would discard the body's named\n // example on the very first open (issue #10075), so only a change between two populated selections\n // counts as a real branch switch.\n const hadNoPreviousSelection = previousSelection === '{}'\n\n // Only a genuine branch switch within the same operation should reset the edited body. An empty\n // selection means there is no composition to switch between (or the selection was cleared on an\n // operation change), so there is nothing to reset.\n if (\n operationChanged ||\n !selectionChanged ||\n hadNoPreviousSelection ||\n Object.keys(requestBodyCompositionSelection ?? {}).length === 0\n ) {\n return\n }\n\n const codec = structuredCodec.value\n const isXml = isXmlMediaType(selectedContentType.value)\n if (!requestBody || (!codec && !isXml)) {\n return\n }\n\n // Regenerate through the same helper the initial example uses so a reset produces exactly the\n // value a fresh render of this branch would, rather than a hand-copied second code path.\n const selectedValue = getSchemaExampleFromBody(\n requestBody,\n selectedContentType.value,\n requestBodyCompositionSelection,\n openapiVersion,\n )\n\n emits('update:value', {\n // A branch with no writable content generates `null`/`undefined`; clear the editor rather than\n // writing the literal text `null` or leaving the previously selected branch's body behind.\n payload:\n selectedValue == null\n ? ''\n : isXml\n ? String(selectedValue)\n : codec!.stringify(selectedValue),\n contentType: selectedContentType.value,\n })\n },\n)\n\n/** The form view only works on an object-shaped body root */\nconst isFormViewAvailable = computed(\n () => parsedBody.value.ok && isObject(parsedBody.value.value),\n)\n\n/**\n * Show the Form/Raw toggle for structured bodies that are (or per schema should be)\n * an object at the root. Non-object roots (arrays, primitives) stay raw-only.\n */\nconst showBodyViewToggle = computed(\n () =>\n Boolean(structuredCodec.value) &&\n (isFormViewAvailable.value ||\n Boolean(bodySchema.value && isObjectSchema(bodySchema.value))),\n)\n\n/** Selected body view, seeded from the document default (raw unless configured) */\nconst bodyView = ref<'form' | 'raw'>(defaultView)\n\n// Fall back to raw when the form view stops being available (e.g. the content type\n// changed to a non-structured one, or an external edit made the body unparseable).\nwatch(isFormViewAvailable, (ok) => {\n if (!ok) {\n bodyView.value = 'raw'\n }\n})\n\n/**\n * Whether we can offer to generate a fresh example from the schema.\n *\n * Only structured (JSON/YAML) bodies qualify: they have a schema to generate from and a text\n * representation we can seed the new example with. Binary and form bodies have their own editors,\n * so the button would have nothing meaningful to write.\n */\nconst canGenerateExample = computed(() =>\n Boolean(structuredCodec.value && bodySchema.value),\n)\n</script>\n<template>\n <CollapsibleSection>\n <template #title>{{ title }}</template>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableHeader\n class=\"relative col-span-full flex h-8 cursor-pointer items-center justify-between border-r-0 !p-0\">\n <ScalarListbox\n v-model=\"selectedContentTypeModel\"\n :options=\"contentTypeOptions\"\n teleport>\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-3 font-normal\"\n fullWidth\n variant=\"ghost\">\n <span>{{ selectedContentTypeModel.label }}</span>\n <ScalarIcon\n icon=\"ChevronDown\"\n size=\"md\" />\n </ScalarButton>\n </ScalarListbox>\n <div class=\"flex items-center gap-1\">\n <!--\n Generates a new example from the schema so the auto-generated fields stay reachable\n even once the document ships custom examples.\n -->\n <ScalarIconButton\n v-if=\"canGenerateExample\"\n aria-label=\"Generate example from schema\"\n :icon=\"ScalarIconMagicWand\"\n label=\"Generate example from schema\"\n size=\"sm\"\n tooltip\n @click.stop=\"\n emits('generate:example', { contentType: selectedContentType })\n \" />\n <RequestBodyViewToggle\n v-if=\"showBodyViewToggle\"\n :disabled=\"!isFormViewAvailable\"\n :modelValue=\"bodyView\"\n @update:modelValue=\"(v) => (bodyView = v)\" />\n </div>\n </DataTableHeader>\n <DataTableRow>\n <!-- No Body -->\n <template v-if=\"selectedContentType === 'none'\">\n <div\n class=\"text-c-3 flex min-h-10 w-full items-center justify-center border-t p-2 text-sm\">\n <span>{{ translate('apiClient.requestBody.noBody') }}</span>\n </div>\n </template>\n\n <!-- Binary File -->\n <template\n v-else-if=\"selectedContentType === 'application/octet-stream'\">\n <div\n class=\"flex items-center justify-center overflow-hidden border-t p-1.5\">\n <template\n v-if=\"\n getFileName(unpackProxyObject(example?.value)) !== undefined\n \">\n <span\n class=\"text-c-2 w-full max-w-full overflow-hidden rounded border px-1.5 py-1 text-xs whitespace-nowrap\">\n {{ getFileName(unpackProxyObject(example?.value)) }}\n </span>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 ml-1 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n emits('update:value', {\n payload: undefined,\n contentType: selectedContentType,\n })\n \">\n {{ translate('apiClient.requestBody.delete') }}\n </ScalarButton>\n </template>\n <template v-else>\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 border-0 shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"\n () =>\n handleFileUpload((file) =>\n emits('update:value', {\n payload: file,\n contentType: selectedContentType,\n }),\n )\n \">\n <span>{{ translate('apiClient.requestBody.selectFile') }}</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </template>\n </div>\n </template>\n\n <!-- Form Data / URL Encoded -->\n <template\n v-else-if=\"\n selectedContentType === 'multipart/form-data' ||\n selectedContentType === 'application/x-www-form-urlencoded'\n \">\n <RequestBodyForm\n :bodySchema\n :environment\n :example\n :selectedContentType\n @update:formValue=\"\n (payload) =>\n emits('update:formValue', {\n payload,\n contentType: selectedContentType,\n })\n \" />\n </template>\n\n <!-- Code/Other -->\n <template v-else>\n <!-- Schema-driven form view for structured (JSON/YAML) bodies -->\n <RequestBodyStructured\n v-if=\"bodyView === 'form' && isFormViewAvailable\"\n :bodySchema\n :contentType=\"selectedContentType\"\n :environment\n :parsedValue=\"parsedBody.value\"\n @update:value=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <!-- Editable raw body with a copy button revealed on hover/focus -->\n <div\n v-else\n class=\"group/code-block relative border-t\">\n <CodeInput\n class=\"px-3\"\n content=\"\"\n :environment=\"environment\"\n :language=\"selectedLanguage\"\n lineNumbers\n lint\n :modelValue=\"bodyValue\"\n withFakeData\n @update:modelValue=\"\n (value) =>\n emits('update:value', {\n payload: value,\n contentType: selectedContentType,\n })\n \" />\n <ScalarCodeBlockCopy\n v-if=\"bodyValue\"\n class=\"absolute top-1.5 right-1.5\"\n :content=\"bodyValue\"\n :lang=\"selectedLanguage\"\n showLang>\n <template #backdrop>\n <ScalarCopyBackdrop class=\"-top-1 -right-1.5\" />\n </template>\n </ScalarCodeBlockCopy>\n </div>\n </template>\n </DataTableRow>\n </DataTable>\n </CollapsibleSection>\n</template>\n<style scoped>\n:deep(.cm-content) {\n font-size: var(--scalar-small);\n}\n</style>\n"],"mappings":"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"}
@@ -12,6 +12,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
12
12
  name: string;
13
13
  value: string | File | unknown[] | undefined;
14
14
  isDisabled: boolean;
15
+ isDisabledByDefault?: boolean;
15
16
  isArray?: boolean;
16
17
  }[]) => any;
17
18
  }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
@@ -19,6 +20,7 @@ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {
19
20
  name: string;
20
21
  value: string | File | unknown[] | undefined;
21
22
  isDisabled: boolean;
23
+ isDisabledByDefault?: boolean;
22
24
  isArray?: boolean;
23
25
  }[]) => any) | undefined;
24
26
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBodyForm.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyForm.vue"],"names":[],"mappings":"AA+IA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACb,MAAM,8DAA8D,CAAA;AAWrE,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAAA;IACzC,6FAA6F;IAC7F,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,kBAAkB,CAAA;CAChC,CAAC;AAmMF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"RequestBodyForm.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyForm.vue"],"names":[],"mappings":"AAkKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AACnH,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACb,MAAM,8DAA8D,CAAA;AAWrE,KAAK,WAAW,GAAG;IACjB,OAAO,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAAA;IACzC,6FAA6F;IAC7F,UAAU,CAAC,EAAE,YAAY,CAAA;IACzB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,kBAAkB,CAAA;CAChC,CAAC;AAsNF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBodyForm.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ExampleObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestTable from '@/v2/blocks/request-block/components/RequestTable.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n getFormBodyRows,\n getFormBodyValue,\n} from '@/v2/blocks/request-block/helpers/get-form-body-rows'\n\nconst { example, bodySchema, selectedContentType, environment } = defineProps<{\n example: ExampleObject | undefined | null\n /** Resolved schema for the form body so the table can show enums and validation per field */\n bodySchema?: SchemaObject\n selectedContentType: string\n environment: XScalarEnvironment\n}>()\n\nconst emit = defineEmits<{\n (\n e: 'update:formValue',\n payload: ApiReferenceEvents['operation:update:requestBody:formValue']['payload'],\n ): void\n}>()\n\n/** Local state for form body rows */\nconst localFormBodyRows = ref<TableRow[]>([])\n\n/** Sync the local form body rows with the example and schema */\nwatch(\n () => [example, bodySchema, selectedContentType] as const,\n ([newExample, schema, contentType]) => {\n localFormBodyRows.value = getFormBodyRows(newExample, contentType, schema)\n },\n { immediate: true },\n)\n\nconst handleUpdateFormValue = (rows: TableRow[]) => {\n emit(\n 'update:formValue',\n rows.map((row) => ({\n name: row.name,\n value:\n selectedContentType === 'multipart/form-data'\n ? getFormBodyValue(row)\n : (row.value as string | File),\n isDisabled: row.isDisabled ?? false,\n ...(selectedContentType === 'multipart/form-data' && row.isArray\n ? { isArray: true }\n : {}),\n })),\n )\n}\n\n/** Update a row in the table, combines with the previous data so we emit a whole row */\nconst handleUpsertRow = (\n index: number,\n payload: Partial<{\n name: string\n value: string | File | undefined\n isDisabled: boolean\n }>,\n) => {\n // Add new row\n if (index >= localFormBodyRows.value.length) {\n localFormBodyRows.value = [\n ...localFormBodyRows.value,\n { name: '', value: '', ...payload, isDisabled: false },\n ]\n handleUpdateFormValue(localFormBodyRows.value)\n return\n }\n\n localFormBodyRows.value = localFormBodyRows.value.map((row, i) =>\n i === index ? { ...row, ...payload } : row,\n )\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Delete a row from the table */\nconst handleDeleteRow = (index: number) => {\n localFormBodyRows.value = localFormBodyRows.value.filter(\n (_, i) => i !== index,\n )\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Handle file upload for a specific row index */\nconst handleFileUpload = (index: number) => {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n\n if (file) {\n const currentRow = localFormBodyRows.value[index]\n handleUpsertRow(index, {\n name: currentRow?.name || file.name,\n value: file,\n })\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n</script>\n\n<template>\n <!-- Form Data -->\n <template v-if=\"selectedContentType === 'multipart/form-data'\">\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n showUploadButton\n @deleteRow=\"handleDeleteRow\"\n @removeFile=\"(index) => handleUpsertRow(index, { value: undefined })\"\n @uploadFile=\"handleFileUpload\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n\n <!-- Form URL Encoded -->\n <template v-else>\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n @deleteRow=\"handleDeleteRow\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RequestBodyForm.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ExampleObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestTable from '@/v2/blocks/request-block/components/RequestTable.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n getFormBodyRows,\n getFormBodyValue,\n} from '@/v2/blocks/request-block/helpers/get-form-body-rows'\n\nconst { example, bodySchema, selectedContentType, environment } = defineProps<{\n example: ExampleObject | undefined | null\n /** Resolved schema for the form body so the table can show enums and validation per field */\n bodySchema?: SchemaObject\n selectedContentType: string\n environment: XScalarEnvironment\n}>()\n\nconst emit = defineEmits<{\n (\n e: 'update:formValue',\n payload: ApiReferenceEvents['operation:update:requestBody:formValue']['payload'],\n ): void\n}>()\n\n/** Local state for form body rows */\nconst localFormBodyRows = ref<TableRow[]>([])\n\n/** Sync the local form body rows with the example and schema */\nwatch(\n () => [example, bodySchema, selectedContentType] as const,\n ([newExample, schema, contentType]) => {\n localFormBodyRows.value = getFormBodyRows(newExample, contentType, schema)\n },\n { immediate: true },\n)\n\nconst handleUpdateFormValue = (rows: TableRow[]) => {\n emit(\n 'update:formValue',\n rows.map((row) => ({\n name: row.name,\n value:\n selectedContentType === 'multipart/form-data'\n ? getFormBodyValue(row)\n : (row.value as string | File),\n isDisabled: row.isDisabled ?? false,\n ...(row.isDisabledByDefault ? { isDisabledByDefault: true } : {}),\n ...(selectedContentType === 'multipart/form-data' && row.isArray\n ? { isArray: true }\n : {}),\n })),\n )\n}\n\n/** Update a row in the table, combines with the previous data so we emit a whole row */\nconst handleUpsertRow = (\n index: number,\n payload: Partial<{\n name: string\n value: string | File | undefined\n isDisabled: boolean\n }>,\n) => {\n // Add new row\n if (index >= localFormBodyRows.value.length) {\n localFormBodyRows.value = [\n ...localFormBodyRows.value,\n { name: '', value: '', ...payload, isDisabled: false },\n ]\n handleUpdateFormValue(localFormBodyRows.value)\n return\n }\n\n localFormBodyRows.value = localFormBodyRows.value.map((row, i) => {\n if (i !== index) {\n return row\n }\n\n const valueChanged = 'value' in payload && payload.value !== row.value\n const disabledChanged =\n payload.isDisabled !== undefined && payload.isDisabled !== row.isDisabled\n\n return {\n ...row,\n ...payload,\n // File selection bypasses the text input, but also expresses intent to send a value.\n ...(valueChanged && payload.value !== undefined && row.isDisabledByDefault\n ? { isDisabled: false }\n : {}),\n // Preserve untouched defaults while keeping deliberate checkbox choices explicit.\n isDisabledByDefault:\n row.isDisabledByDefault && !valueChanged && !disabledChanged,\n }\n })\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Delete a row from the table */\nconst handleDeleteRow = (index: number) => {\n localFormBodyRows.value = localFormBodyRows.value.filter(\n (_, i) => i !== index,\n )\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Handle file upload for a specific row index */\nconst handleFileUpload = (index: number) => {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n\n if (file) {\n const currentRow = localFormBodyRows.value[index]\n handleUpsertRow(index, {\n name: currentRow?.name || file.name,\n value: file,\n })\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n</script>\n\n<template>\n <!-- Form Data -->\n <template v-if=\"selectedContentType === 'multipart/form-data'\">\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n showUploadButton\n @deleteRow=\"handleDeleteRow\"\n @removeFile=\"(index) => handleUpsertRow(index, { value: undefined })\"\n @uploadFile=\"handleFileUpload\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n\n <!-- Form URL Encoded -->\n <template v-else>\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n @deleteRow=\"handleDeleteRow\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n</template>\n"],"mappings":""}
@@ -29,6 +29,7 @@ var RequestBodyForm_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
29
29
  name: row.name,
30
30
  value: __props.selectedContentType === "multipart/form-data" ? getFormBodyValue(row) : row.value,
31
31
  isDisabled: row.isDisabled ?? false,
32
+ ...row.isDisabledByDefault ? { isDisabledByDefault: true } : {},
32
33
  ...__props.selectedContentType === "multipart/form-data" && row.isArray ? { isArray: true } : {}
33
34
  })));
34
35
  };
@@ -44,10 +45,17 @@ var RequestBodyForm_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
44
45
  handleUpdateFormValue(localFormBodyRows.value);
45
46
  return;
46
47
  }
47
- localFormBodyRows.value = localFormBodyRows.value.map((row, i) => i === index ? {
48
- ...row,
49
- ...payload
50
- } : row);
48
+ localFormBodyRows.value = localFormBodyRows.value.map((row, i) => {
49
+ if (i !== index) return row;
50
+ const valueChanged = "value" in payload && payload.value !== row.value;
51
+ const disabledChanged = payload.isDisabled !== void 0 && payload.isDisabled !== row.isDisabled;
52
+ return {
53
+ ...row,
54
+ ...payload,
55
+ ...valueChanged && payload.value !== void 0 && row.isDisabledByDefault ? { isDisabled: false } : {},
56
+ isDisabledByDefault: row.isDisabledByDefault && !valueChanged && !disabledChanged
57
+ };
58
+ });
51
59
  handleUpdateFormValue(localFormBodyRows.value);
52
60
  };
53
61
  /** Delete a row from the table */
@@ -1 +1 @@
1
- {"version":3,"file":"RequestBodyForm.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ExampleObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestTable from '@/v2/blocks/request-block/components/RequestTable.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n getFormBodyRows,\n getFormBodyValue,\n} from '@/v2/blocks/request-block/helpers/get-form-body-rows'\n\nconst { example, bodySchema, selectedContentType, environment } = defineProps<{\n example: ExampleObject | undefined | null\n /** Resolved schema for the form body so the table can show enums and validation per field */\n bodySchema?: SchemaObject\n selectedContentType: string\n environment: XScalarEnvironment\n}>()\n\nconst emit = defineEmits<{\n (\n e: 'update:formValue',\n payload: ApiReferenceEvents['operation:update:requestBody:formValue']['payload'],\n ): void\n}>()\n\n/** Local state for form body rows */\nconst localFormBodyRows = ref<TableRow[]>([])\n\n/** Sync the local form body rows with the example and schema */\nwatch(\n () => [example, bodySchema, selectedContentType] as const,\n ([newExample, schema, contentType]) => {\n localFormBodyRows.value = getFormBodyRows(newExample, contentType, schema)\n },\n { immediate: true },\n)\n\nconst handleUpdateFormValue = (rows: TableRow[]) => {\n emit(\n 'update:formValue',\n rows.map((row) => ({\n name: row.name,\n value:\n selectedContentType === 'multipart/form-data'\n ? getFormBodyValue(row)\n : (row.value as string | File),\n isDisabled: row.isDisabled ?? false,\n ...(selectedContentType === 'multipart/form-data' && row.isArray\n ? { isArray: true }\n : {}),\n })),\n )\n}\n\n/** Update a row in the table, combines with the previous data so we emit a whole row */\nconst handleUpsertRow = (\n index: number,\n payload: Partial<{\n name: string\n value: string | File | undefined\n isDisabled: boolean\n }>,\n) => {\n // Add new row\n if (index >= localFormBodyRows.value.length) {\n localFormBodyRows.value = [\n ...localFormBodyRows.value,\n { name: '', value: '', ...payload, isDisabled: false },\n ]\n handleUpdateFormValue(localFormBodyRows.value)\n return\n }\n\n localFormBodyRows.value = localFormBodyRows.value.map((row, i) =>\n i === index ? { ...row, ...payload } : row,\n )\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Delete a row from the table */\nconst handleDeleteRow = (index: number) => {\n localFormBodyRows.value = localFormBodyRows.value.filter(\n (_, i) => i !== index,\n )\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Handle file upload for a specific row index */\nconst handleFileUpload = (index: number) => {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n\n if (file) {\n const currentRow = localFormBodyRows.value[index]\n handleUpsertRow(index, {\n name: currentRow?.name || file.name,\n value: file,\n })\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n</script>\n\n<template>\n <!-- Form Data -->\n <template v-if=\"selectedContentType === 'multipart/form-data'\">\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n showUploadButton\n @deleteRow=\"handleDeleteRow\"\n @removeFile=\"(index) => handleUpsertRow(index, { value: undefined })\"\n @uploadFile=\"handleFileUpload\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n\n <!-- Form URL Encoded -->\n <template v-else>\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n @deleteRow=\"handleDeleteRow\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;EAyBA,MAAM,OAAO;;EAQb,MAAM,oBAAoB,IAAgB,CAAC,CAAC;;EAG5C,YACQ;GAAC,QAAA;GAAS,QAAA;GAAY,QAAA;EAAmB,IAC9C,CAAC,YAAY,QAAQ,iBAAiB;GACrC,kBAAkB,QAAQ,gBAAgB,YAAY,aAAa,MAAM;EAC3E,GACA,EAAE,WAAW,KAAK,CACpB;EAEA,MAAM,yBAAyB,SAAqB;GAClD,KACE,oBACA,KAAK,KAAK,SAAS;IACjB,MAAM,IAAI;IACV,OACE,QAAA,wBAAwB,wBACpB,iBAAiB,GAAG,IACnB,IAAI;IACX,YAAY,IAAI,cAAc;IAC9B,GAAI,QAAA,wBAAwB,yBAAyB,IAAI,UACrD,EAAE,SAAS,KAAK,IAChB,CAAC;GACP,EAAE,CACJ;EACF;;EAGA,MAAM,mBACJ,OACA,YAKG;GAEH,IAAI,SAAS,kBAAkB,MAAM,QAAQ;IAC3C,kBAAkB,QAAQ,CACxB,GAAG,kBAAkB,OACrB;KAAE,MAAM;KAAI,OAAO;KAAI,GAAG;KAAS,YAAY;IAAM,CACvD;IACA,sBAAsB,kBAAkB,KAAK;IAC7C;GACF;GAEA,kBAAkB,QAAQ,kBAAkB,MAAM,KAAK,KAAK,MAC1D,MAAM,QAAQ;IAAE,GAAG;IAAK,GAAG;GAAQ,IAAI,GACzC;GACA,sBAAsB,kBAAkB,KAAK;EAC/C;;EAGA,MAAM,mBAAmB,UAAkB;GACzC,kBAAkB,QAAQ,kBAAkB,MAAM,QAC/C,GAAG,MAAM,MAAM,KAClB;GACA,sBAAsB,kBAAkB,KAAK;EAC/C;;EAGA,MAAM,oBAAoB,UAAkB;GAC1C,MAAM,EAAE,SAAS,cAAc;IAC7B,WAAW,UAAU;KACnB,MAAM,OAAO,QAAQ;KAErB,IAAI,MAAM;MACR,MAAM,aAAa,kBAAkB,MAAM;MAC3C,gBAAgB,OAAO;OACrB,MAAM,YAAY,QAAQ,KAAK;OAC/B,OAAO;MACT,CAAC;KACH;IACF;IACA,UAAU;IACV,QAAQ;GACV,CAAC;GACD,KAAK;EACP;;UAKkB,QAAA,wBAAmB,yBAAA,UAAA,GACjC,YAQiC,sBAAA;;IAP9B,MAAM,kBAAA;IACP,iBAAA;IACC,aAAa,QAAA;IACd,kBAAA;IACC,aAAW;IACX,cAAU,OAAA,OAAA,OAAA,MAAG,UAAU,gBAAgB,OAAK,EAAA,OAAW,KAAA,EAAS,CAAA;IAChE,cAAY;IACZ,aAAW;yDAKd,YAKiC,sBAAA;;IAJ9B,MAAM,kBAAA;IACP,iBAAA;IACC,aAAa,QAAA;IACb,aAAW;IACX,aAAW"}
1
+ {"version":3,"file":"RequestBodyForm.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestBodyForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ExampleObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { ref, watch } from 'vue'\n\nimport { useFileDialog } from '@/hooks/use-file-dialog'\nimport RequestTable from '@/v2/blocks/request-block/components/RequestTable.vue'\nimport type { TableRow } from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n getFormBodyRows,\n getFormBodyValue,\n} from '@/v2/blocks/request-block/helpers/get-form-body-rows'\n\nconst { example, bodySchema, selectedContentType, environment } = defineProps<{\n example: ExampleObject | undefined | null\n /** Resolved schema for the form body so the table can show enums and validation per field */\n bodySchema?: SchemaObject\n selectedContentType: string\n environment: XScalarEnvironment\n}>()\n\nconst emit = defineEmits<{\n (\n e: 'update:formValue',\n payload: ApiReferenceEvents['operation:update:requestBody:formValue']['payload'],\n ): void\n}>()\n\n/** Local state for form body rows */\nconst localFormBodyRows = ref<TableRow[]>([])\n\n/** Sync the local form body rows with the example and schema */\nwatch(\n () => [example, bodySchema, selectedContentType] as const,\n ([newExample, schema, contentType]) => {\n localFormBodyRows.value = getFormBodyRows(newExample, contentType, schema)\n },\n { immediate: true },\n)\n\nconst handleUpdateFormValue = (rows: TableRow[]) => {\n emit(\n 'update:formValue',\n rows.map((row) => ({\n name: row.name,\n value:\n selectedContentType === 'multipart/form-data'\n ? getFormBodyValue(row)\n : (row.value as string | File),\n isDisabled: row.isDisabled ?? false,\n ...(row.isDisabledByDefault ? { isDisabledByDefault: true } : {}),\n ...(selectedContentType === 'multipart/form-data' && row.isArray\n ? { isArray: true }\n : {}),\n })),\n )\n}\n\n/** Update a row in the table, combines with the previous data so we emit a whole row */\nconst handleUpsertRow = (\n index: number,\n payload: Partial<{\n name: string\n value: string | File | undefined\n isDisabled: boolean\n }>,\n) => {\n // Add new row\n if (index >= localFormBodyRows.value.length) {\n localFormBodyRows.value = [\n ...localFormBodyRows.value,\n { name: '', value: '', ...payload, isDisabled: false },\n ]\n handleUpdateFormValue(localFormBodyRows.value)\n return\n }\n\n localFormBodyRows.value = localFormBodyRows.value.map((row, i) => {\n if (i !== index) {\n return row\n }\n\n const valueChanged = 'value' in payload && payload.value !== row.value\n const disabledChanged =\n payload.isDisabled !== undefined && payload.isDisabled !== row.isDisabled\n\n return {\n ...row,\n ...payload,\n // File selection bypasses the text input, but also expresses intent to send a value.\n ...(valueChanged && payload.value !== undefined && row.isDisabledByDefault\n ? { isDisabled: false }\n : {}),\n // Preserve untouched defaults while keeping deliberate checkbox choices explicit.\n isDisabledByDefault:\n row.isDisabledByDefault && !valueChanged && !disabledChanged,\n }\n })\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Delete a row from the table */\nconst handleDeleteRow = (index: number) => {\n localFormBodyRows.value = localFormBodyRows.value.filter(\n (_, i) => i !== index,\n )\n handleUpdateFormValue(localFormBodyRows.value)\n}\n\n/** Handle file upload for a specific row index */\nconst handleFileUpload = (index: number) => {\n const { open } = useFileDialog({\n onChange: (files) => {\n const file = files?.[0]\n\n if (file) {\n const currentRow = localFormBodyRows.value[index]\n handleUpsertRow(index, {\n name: currentRow?.name || file.name,\n value: file,\n })\n }\n },\n multiple: false,\n accept: '*/*',\n })\n open()\n}\n</script>\n\n<template>\n <!-- Form Data -->\n <template v-if=\"selectedContentType === 'multipart/form-data'\">\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n showUploadButton\n @deleteRow=\"handleDeleteRow\"\n @removeFile=\"(index) => handleUpsertRow(index, { value: undefined })\"\n @uploadFile=\"handleFileUpload\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n\n <!-- Form URL Encoded -->\n <template v-else>\n <RequestTable\n :data=\"localFormBodyRows\"\n deferKeyUpdates\n :environment=\"environment\"\n @deleteRow=\"handleDeleteRow\"\n @upsertRow=\"handleUpsertRow\" />\n </template>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;EAyBA,MAAM,OAAO;;EAQb,MAAM,oBAAoB,IAAgB,CAAC,CAAC;;EAG5C,YACQ;GAAC,QAAA;GAAS,QAAA;GAAY,QAAA;EAAmB,IAC9C,CAAC,YAAY,QAAQ,iBAAiB;GACrC,kBAAkB,QAAQ,gBAAgB,YAAY,aAAa,MAAM;EAC3E,GACA,EAAE,WAAW,KAAK,CACpB;EAEA,MAAM,yBAAyB,SAAqB;GAClD,KACE,oBACA,KAAK,KAAK,SAAS;IACjB,MAAM,IAAI;IACV,OACE,QAAA,wBAAwB,wBACpB,iBAAiB,GAAG,IACnB,IAAI;IACX,YAAY,IAAI,cAAc;IAC9B,GAAI,IAAI,sBAAsB,EAAE,qBAAqB,KAAK,IAAI,CAAC;IAC/D,GAAI,QAAA,wBAAwB,yBAAyB,IAAI,UACrD,EAAE,SAAS,KAAK,IAChB,CAAC;GACP,EAAE,CACJ;EACF;;EAGA,MAAM,mBACJ,OACA,YAKG;GAEH,IAAI,SAAS,kBAAkB,MAAM,QAAQ;IAC3C,kBAAkB,QAAQ,CACxB,GAAG,kBAAkB,OACrB;KAAE,MAAM;KAAI,OAAO;KAAI,GAAG;KAAS,YAAY;IAAM,CACvD;IACA,sBAAsB,kBAAkB,KAAK;IAC7C;GACF;GAEA,kBAAkB,QAAQ,kBAAkB,MAAM,KAAK,KAAK,MAAM;IAChE,IAAI,MAAM,OACR,OAAO;IAGT,MAAM,eAAe,WAAW,WAAW,QAAQ,UAAU,IAAI;IACjE,MAAM,kBACJ,QAAQ,eAAe,KAAA,KAAa,QAAQ,eAAe,IAAI;IAEjE,OAAO;KACL,GAAG;KACH,GAAG;KAEH,GAAI,gBAAgB,QAAQ,UAAU,KAAA,KAAa,IAAI,sBACnD,EAAE,YAAY,MAAM,IACpB,CAAC;KAEL,qBACE,IAAI,uBAAuB,CAAC,gBAAgB,CAAC;IACjD;GACF,CAAC;GACD,sBAAsB,kBAAkB,KAAK;EAC/C;;EAGA,MAAM,mBAAmB,UAAkB;GACzC,kBAAkB,QAAQ,kBAAkB,MAAM,QAC/C,GAAG,MAAM,MAAM,KAClB;GACA,sBAAsB,kBAAkB,KAAK;EAC/C;;EAGA,MAAM,oBAAoB,UAAkB;GAC1C,MAAM,EAAE,SAAS,cAAc;IAC7B,WAAW,UAAU;KACnB,MAAM,OAAO,QAAQ;KAErB,IAAI,MAAM;MACR,MAAM,aAAa,kBAAkB,MAAM;MAC3C,gBAAgB,OAAO;OACrB,MAAM,YAAY,QAAQ,KAAK;OAC/B,OAAO;MACT,CAAC;KACH;IACF;IACA,UAAU;IACV,QAAQ;GACV,CAAC;GACD,KAAK;EACP;;UAKkB,QAAA,wBAAmB,yBAAA,UAAA,GACjC,YAQiC,sBAAA;;IAP9B,MAAM,kBAAA;IACP,iBAAA;IACC,aAAa,QAAA;IACd,kBAAA;IACC,aAAW;IACX,cAAU,OAAA,OAAA,OAAA,MAAG,UAAU,gBAAgB,OAAK,EAAA,OAAW,KAAA,EAAS,CAAA;IAChE,cAAY;IACZ,aAAW;yDAKd,YAKiC,sBAAA;;IAJ9B,MAAM,kBAAA;IACP,iBAAA;IACC,aAAa,QAAA;IACb,aAAW;IACX,aAAW"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestCodeSnippet.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestCodeSnippet.vue"],"names":[],"mappings":"AA8KA,OAAO,EAOL,KAAK,gBAAgB,EAEtB,MAAM,6BAA6B,CAAA;AAMpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAOvE,KAAK,WAAW,GAAG,gBAAgB,GAAG;IAAE,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAAC;AA4TtE,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"RequestCodeSnippet.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestCodeSnippet.vue"],"names":[],"mappings":"AAkLA,OAAO,EAOL,KAAK,gBAAgB,EAEtB,MAAM,6BAA6B,CAAA;AAMpC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAOvE,KAAK,WAAW,GAAG,gBAAgB,GAAG;IAAE,QAAQ,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAgUtE,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestCodeSnippet.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestCodeSnippet.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n findClient,\n generateCodeSnippet,\n getClients,\n getCustomCodeSamples,\n getSecrets,\n type ClientOption,\n type CodeExampleProps,\n type CustomClientOption,\n} from '@scalar/blocks/code-example'\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarCodeBlock } from '@scalar/components/code-block'\nimport { ScalarCombobox } from '@scalar/components/combobox'\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, watch } from 'vue'\n\nimport { DataTable, DataTableRow } from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n clientOptions,\n eventBus,\n operation,\n method,\n path,\n selectedContentType,\n selectedServer = null,\n selectedExample,\n securitySchemes,\n selectedClient,\n globalCookies,\n integration,\n} = defineProps<CodeExampleProps & { eventBus: WorkspaceEventBus }>()\n\nconst { translate } = useLocalization()\n\n/** Grab any custom code samples from the operation */\nconst customCodeSamples = computed(() => getCustomCodeSamples(operation))\n\n/** Merge custom code samples with the client options */\nconst clients = computed(() =>\n getClients(\n customCodeSamples.value.samples,\n clientOptions,\n customCodeSamples.value.label,\n ),\n)\n\n/**\n * The locally selected client which would include code samples from this operation only\n * Must be local state because it contains custom code samples from this operation only\n */\nconst localSelectedClient = ref<ClientOption | CustomClientOption | undefined>(\n findClient(clients.value, selectedClient),\n)\n\n/**\n * Re-resolve the local client whenever the global selection or the available\n * clients change. Watching `clients` matters when navigating between operations:\n * the stored id stays the same, but the matching option (e.g. a custom sample)\n * differs per operation, so without this the snippet could go stale.\n */\nwatch([() => selectedClient, clients], ([newClient]) => {\n const client = findClient(clients.value, newClient)\n if (client) {\n localSelectedClient.value = client\n }\n})\n\n/** Block secrets from being shown in the code block */\nconst secretCredentials = computed(() => getSecrets(securitySchemes ?? []))\n\n/** Handle client change */\nconst handleClientChange = (option: ClientOption | undefined) => {\n localSelectedClient.value = option\n\n // Sync the selection globally (built-in client or custom sample keyed by language)\n if (option) {\n eventBus.emit('workspace:update:selected-client', option.id)\n }\n}\n\n/** Generate the code snippet for the selected example */\nconst generatedCode = computed<string | null>(() =>\n generateCodeSnippet({\n defaultDisabledParameters: true,\n clientId: localSelectedClient.value?.id,\n customCodeSamples: customCodeSamples.value.samples,\n operation,\n method,\n path,\n contentType: selectedContentType,\n server: selectedServer,\n securitySchemes,\n example: selectedExample,\n globalCookies,\n includeDefaultHeaders: integration === 'client',\n }),\n)\n\n/** Check if there are any clients available (built-in or custom code samples) */\nconst hasClients = computed(() =>\n clients.value.some((group) => group.options.length > 0),\n)\n</script>\n\n<template>\n <CollapsibleSection\n v-if=\"hasClients\"\n class=\"group/preview w-full border-t\"\n :defaultOpen=\"false\">\n <template #title>\n {{ translate('apiClient.requestCodeSnippet.codeSnippet') }}\n </template>\n\n <!-- Client selector -->\n <template #actions>\n <div class=\"flex flex-1\">\n <ScalarCombobox\n :modelValue=\"localSelectedClient\"\n :options=\"clients\"\n placement=\"bottom-end\"\n @update:modelValue=\"\n (ev) => handleClientChange(ev as ClientOption | undefined)\n \">\n <template #default=\"{ open }\">\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\"\n variant=\"ghost\">\n {{ localSelectedClient?.title }}\n <ScalarIconCaretDown\n class=\"mt-0.25 size-3 transition-transform duration-100\"\n :class=\"open && 'rotate-180'\"\n weight=\"bold\" />\n </ScalarButton>\n </template>\n </ScalarCombobox>\n </div>\n </template>\n\n <!-- Code snippet -->\n <ScalarErrorBoundary>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableRow>\n <div class=\"overflow-hidden\">\n <div\n v-if=\"generatedCode === null\"\n class=\"text-c-2 p-4\"\n role=\"status\">\n {{ translate('apiClient.requestCodeSnippet.unavailable') }}\n </div>\n <ScalarCodeBlock\n v-else\n class=\"text-base\"\n :content=\"generatedCode\"\n :hideCredentials=\"secretCredentials\"\n :lang=\"localSelectedClient?.lang ?? 'plaintext'\"\n lineNumbers />\n </div>\n </DataTableRow>\n </DataTable>\n </ScalarErrorBoundary>\n </CollapsibleSection>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RequestCodeSnippet.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestCodeSnippet.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n findClient,\n generateCodeSnippet,\n getClients,\n getCustomCodeSamples,\n getSecrets,\n type ClientOption,\n type CodeExampleProps,\n type CustomClientOption,\n} from '@scalar/blocks/code-example'\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarCodeBlock } from '@scalar/components/code-block'\nimport { ScalarCombobox } from '@scalar/components/combobox'\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, watch } from 'vue'\n\nimport { DataTable, DataTableRow } from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n clientOptions,\n eventBus,\n operation,\n method,\n path,\n selectedContentType,\n selectedServer = null,\n selectedExample,\n securitySchemes,\n selectedClient,\n globalCookies,\n openapiVersion,\n requestBodyCompositionSelection,\n integration,\n} = defineProps<CodeExampleProps & { eventBus: WorkspaceEventBus }>()\n\nconst { translate } = useLocalization()\n\n/** Grab any custom code samples from the operation */\nconst customCodeSamples = computed(() => getCustomCodeSamples(operation))\n\n/** Merge custom code samples with the client options */\nconst clients = computed(() =>\n getClients(\n customCodeSamples.value.samples,\n clientOptions,\n customCodeSamples.value.label,\n ),\n)\n\n/**\n * The locally selected client which would include code samples from this operation only\n * Must be local state because it contains custom code samples from this operation only\n */\nconst localSelectedClient = ref<ClientOption | CustomClientOption | undefined>(\n findClient(clients.value, selectedClient),\n)\n\n/**\n * Re-resolve the local client whenever the global selection or the available\n * clients change. Watching `clients` matters when navigating between operations:\n * the stored id stays the same, but the matching option (e.g. a custom sample)\n * differs per operation, so without this the snippet could go stale.\n */\nwatch([() => selectedClient, clients], ([newClient]) => {\n const client = findClient(clients.value, newClient)\n if (client) {\n localSelectedClient.value = client\n }\n})\n\n/** Block secrets from being shown in the code block */\nconst secretCredentials = computed(() => getSecrets(securitySchemes ?? []))\n\n/** Handle client change */\nconst handleClientChange = (option: ClientOption | undefined) => {\n localSelectedClient.value = option\n\n // Sync the selection globally (built-in client or custom sample keyed by language)\n if (option) {\n eventBus.emit('workspace:update:selected-client', option.id)\n }\n}\n\n/** Generate the code snippet for the selected example */\nconst generatedCode = computed<string | null>(() =>\n generateCodeSnippet({\n defaultDisabledParameters: true,\n clientId: localSelectedClient.value?.id,\n customCodeSamples: customCodeSamples.value.samples,\n operation,\n method,\n path,\n contentType: selectedContentType,\n server: selectedServer,\n securitySchemes,\n example: selectedExample,\n globalCookies,\n openapiVersion,\n requestBodyCompositionSelection,\n includeDefaultHeaders: integration === 'client',\n }),\n)\n\n/** Check if there are any clients available (built-in or custom code samples) */\nconst hasClients = computed(() =>\n clients.value.some((group) => group.options.length > 0),\n)\n</script>\n\n<template>\n <CollapsibleSection\n v-if=\"hasClients\"\n class=\"group/preview w-full border-t\"\n :defaultOpen=\"false\">\n <template #title>\n {{ translate('apiClient.requestCodeSnippet.codeSnippet') }}\n </template>\n\n <!-- Client selector -->\n <template #actions>\n <div class=\"flex flex-1\">\n <ScalarCombobox\n :modelValue=\"localSelectedClient\"\n :options=\"clients\"\n placement=\"bottom-end\"\n @update:modelValue=\"\n (ev) => handleClientChange(ev as ClientOption | undefined)\n \">\n <template #default=\"{ open }\">\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\"\n variant=\"ghost\">\n {{ localSelectedClient?.title }}\n <ScalarIconCaretDown\n class=\"mt-0.25 size-3 transition-transform duration-100\"\n :class=\"open && 'rotate-180'\"\n weight=\"bold\" />\n </ScalarButton>\n </template>\n </ScalarCombobox>\n </div>\n </template>\n\n <!-- Code snippet -->\n <ScalarErrorBoundary>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableRow>\n <div class=\"overflow-hidden\">\n <div\n v-if=\"generatedCode === null\"\n class=\"text-c-2 p-4\"\n role=\"status\">\n {{ translate('apiClient.requestCodeSnippet.unavailable') }}\n </div>\n <ScalarCodeBlock\n v-else\n class=\"text-base\"\n :content=\"generatedCode\"\n :hideCredentials=\"secretCredentials\"\n :lang=\"localSelectedClient?.lang ?? 'plaintext'\"\n lineNumbers />\n </div>\n </DataTableRow>\n </DataTable>\n </ScalarErrorBoundary>\n </CollapsibleSection>\n</template>\n"],"mappings":""}
@@ -36,6 +36,7 @@ var RequestCodeSnippet_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
36
36
  generateLabel: { type: Function },
37
37
  isWebhook: { type: Boolean },
38
38
  globalCookies: {},
39
+ openapiVersion: {},
39
40
  requestBodyCompositionSelection: {}
40
41
  },
41
42
  setup(__props) {
@@ -79,6 +80,8 @@ var RequestCodeSnippet_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
79
80
  securitySchemes: __props.securitySchemes,
80
81
  example: __props.selectedExample,
81
82
  globalCookies: __props.globalCookies,
83
+ openapiVersion: __props.openapiVersion,
84
+ requestBodyCompositionSelection: __props.requestBodyCompositionSelection,
82
85
  includeDefaultHeaders: __props.integration === "client"
83
86
  }));
84
87
  /** Check if there are any clients available (built-in or custom code samples) */
@@ -1 +1 @@
1
- {"version":3,"file":"RequestCodeSnippet.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestCodeSnippet.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n findClient,\n generateCodeSnippet,\n getClients,\n getCustomCodeSamples,\n getSecrets,\n type ClientOption,\n type CodeExampleProps,\n type CustomClientOption,\n} from '@scalar/blocks/code-example'\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarCodeBlock } from '@scalar/components/code-block'\nimport { ScalarCombobox } from '@scalar/components/combobox'\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, watch } from 'vue'\n\nimport { DataTable, DataTableRow } from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n clientOptions,\n eventBus,\n operation,\n method,\n path,\n selectedContentType,\n selectedServer = null,\n selectedExample,\n securitySchemes,\n selectedClient,\n globalCookies,\n integration,\n} = defineProps<CodeExampleProps & { eventBus: WorkspaceEventBus }>()\n\nconst { translate } = useLocalization()\n\n/** Grab any custom code samples from the operation */\nconst customCodeSamples = computed(() => getCustomCodeSamples(operation))\n\n/** Merge custom code samples with the client options */\nconst clients = computed(() =>\n getClients(\n customCodeSamples.value.samples,\n clientOptions,\n customCodeSamples.value.label,\n ),\n)\n\n/**\n * The locally selected client which would include code samples from this operation only\n * Must be local state because it contains custom code samples from this operation only\n */\nconst localSelectedClient = ref<ClientOption | CustomClientOption | undefined>(\n findClient(clients.value, selectedClient),\n)\n\n/**\n * Re-resolve the local client whenever the global selection or the available\n * clients change. Watching `clients` matters when navigating between operations:\n * the stored id stays the same, but the matching option (e.g. a custom sample)\n * differs per operation, so without this the snippet could go stale.\n */\nwatch([() => selectedClient, clients], ([newClient]) => {\n const client = findClient(clients.value, newClient)\n if (client) {\n localSelectedClient.value = client\n }\n})\n\n/** Block secrets from being shown in the code block */\nconst secretCredentials = computed(() => getSecrets(securitySchemes ?? []))\n\n/** Handle client change */\nconst handleClientChange = (option: ClientOption | undefined) => {\n localSelectedClient.value = option\n\n // Sync the selection globally (built-in client or custom sample keyed by language)\n if (option) {\n eventBus.emit('workspace:update:selected-client', option.id)\n }\n}\n\n/** Generate the code snippet for the selected example */\nconst generatedCode = computed<string | null>(() =>\n generateCodeSnippet({\n defaultDisabledParameters: true,\n clientId: localSelectedClient.value?.id,\n customCodeSamples: customCodeSamples.value.samples,\n operation,\n method,\n path,\n contentType: selectedContentType,\n server: selectedServer,\n securitySchemes,\n example: selectedExample,\n globalCookies,\n includeDefaultHeaders: integration === 'client',\n }),\n)\n\n/** Check if there are any clients available (built-in or custom code samples) */\nconst hasClients = computed(() =>\n clients.value.some((group) => group.options.length > 0),\n)\n</script>\n\n<template>\n <CollapsibleSection\n v-if=\"hasClients\"\n class=\"group/preview w-full border-t\"\n :defaultOpen=\"false\">\n <template #title>\n {{ translate('apiClient.requestCodeSnippet.codeSnippet') }}\n </template>\n\n <!-- Client selector -->\n <template #actions>\n <div class=\"flex flex-1\">\n <ScalarCombobox\n :modelValue=\"localSelectedClient\"\n :options=\"clients\"\n placement=\"bottom-end\"\n @update:modelValue=\"\n (ev) => handleClientChange(ev as ClientOption | undefined)\n \">\n <template #default=\"{ open }\">\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\"\n variant=\"ghost\">\n {{ localSelectedClient?.title }}\n <ScalarIconCaretDown\n class=\"mt-0.25 size-3 transition-transform duration-100\"\n :class=\"open && 'rotate-180'\"\n weight=\"bold\" />\n </ScalarButton>\n </template>\n </ScalarCombobox>\n </div>\n </template>\n\n <!-- Code snippet -->\n <ScalarErrorBoundary>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableRow>\n <div class=\"overflow-hidden\">\n <div\n v-if=\"generatedCode === null\"\n class=\"text-c-2 p-4\"\n role=\"status\">\n {{ translate('apiClient.requestCodeSnippet.unavailable') }}\n </div>\n <ScalarCodeBlock\n v-else\n class=\"text-base\"\n :content=\"generatedCode\"\n :hideCredentials=\"secretCredentials\"\n :lang=\"localSelectedClient?.lang ?? 'plaintext'\"\n lineNumbers />\n </div>\n </DataTableRow>\n </DataTable>\n </ScalarErrorBoundary>\n </CollapsibleSection>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCA,MAAM,EAAE,cAAc,gBAAgB;;EAGtC,MAAM,oBAAoB,eAAe,qBAAqB,QAAA,SAAS,CAAC;;EAGxE,MAAM,UAAU,eACd,WACE,kBAAkB,MAAM,SACxB,QAAA,eACA,kBAAkB,MAAM,KAC1B,CACF;;;;;EAMA,MAAM,sBAAsB,IAC1B,WAAW,QAAQ,OAAO,QAAA,cAAc,CAC1C;;;;;;;EAQA,MAAM,OAAO,QAAA,gBAAgB,OAAO,IAAI,CAAC,eAAe;GACtD,MAAM,SAAS,WAAW,QAAQ,OAAO,SAAS;GAClD,IAAI,QACF,oBAAoB,QAAQ;EAEhC,CAAC;;EAGD,MAAM,oBAAoB,eAAe,WAAW,QAAA,mBAAmB,CAAC,CAAC,CAAC;;EAG1E,MAAM,sBAAsB,WAAqC;GAC/D,oBAAoB,QAAQ;GAG5B,IAAI,QACF,QAAA,SAAS,KAAK,oCAAoC,OAAO,EAAE;EAE/D;;EAGA,MAAM,gBAAgB,eACpB,oBAAoB;GAClB,2BAA2B;GAC3B,UAAU,oBAAoB,OAAO;GACrC,mBAAmB,kBAAkB,MAAM;GAC3C,WAAQ,QAAA;GACR,QAAK,QAAA;GACL,MAAG,QAAA;GACH,aAAa,QAAA;GACb,QAAQ,QAAA;GACR,iBAAc,QAAA;GACd,SAAS,QAAA;GACT,eAAY,QAAA;GACZ,uBAAuB,QAAA,gBAAgB;EACzC,CAAC,CACH;;EAGA,MAAM,aAAa,eACjB,QAAQ,MAAM,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,CACxD;;UAKU,WAAA,SAAA,UAAA,GADR,YA0DqB,MAAA,0BAAA,GAAA;;IAxDnB,OAAM;IACL,aAAa;;IACH,OAAK,cAC6C,CAAA,gBAAA,gBAAxD,MAAA,SAAA,CAAS,CAAA,0CAAA,CAAA,GAAA,CAAA,CAAA,CAAA;IAIH,SAAO,cAsBV,CArBN,mBAqBM,OArBN,YAqBM,CApBJ,YAmBiB,MAAA,cAAA,GAAA;KAlBd,YAAY,oBAAA;KACZ,SAAS,QAAA;KACV,WAAU;KACT,uBAAiB,OAAA,OAAA,OAAA,MAAgB,OAAO,mBAAmB,EAAE;;KAGnD,SAAO,SAUD,EAVK,WAAI,CACxB,YASe,MAAA,YAAA,GAAA;MARb,OAAM;MACN,eAAY;MACZ,SAAQ;;6BACwB,CAAA,gBAAA,gBAA7B,oBAAA,OAAqB,KAAK,IAAG,KAChC,CAAA,GAAA,YAGkB,MAAA,mBAAA,GAAA;OAFhB,OAAK,eAAA,CAAC,oDACE,QAAI,YAAA,CAAA;OACZ,QAAO;;;;;;2BA8BG,CAtBtB,YAsBsB,MAAA,mBAAA,GAAA,MAAA;4BADR,CApBZ,YAoBY,MAAA,iBAAA,GAAA;MAnBT,SAAS,CAAA,EAAA;MACV,gBAAA;;6BAiBe,CAhBf,YAgBe,MAAA,oBAAA,GAAA,MAAA;8BADP,CAdN,mBAcM,OAdN,YAcM,CAZI,cAAA,UAAa,QAAA,UAAA,GADrB,mBAKM,OALN,YAKM,gBADD,MAAA,SAAA,CAAS,CAAA,0CAAA,CAAA,GAAA,CAAA,MAAA,UAAA,GAEd,YAMgB,MAAA,eAAA,GAAA;;QAJd,OAAM;QACL,SAAS,cAAA;QACT,iBAAiB,kBAAA;QACjB,MAAM,oBAAA,OAAqB,QAAI;QAChC,aAAA"}
1
+ {"version":3,"file":"RequestCodeSnippet.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestCodeSnippet.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n findClient,\n generateCodeSnippet,\n getClients,\n getCustomCodeSamples,\n getSecrets,\n type ClientOption,\n type CodeExampleProps,\n type CustomClientOption,\n} from '@scalar/blocks/code-example'\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarCodeBlock } from '@scalar/components/code-block'\nimport { ScalarCombobox } from '@scalar/components/combobox'\nimport { ScalarErrorBoundary } from '@scalar/components/error-boundary'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, watch } from 'vue'\n\nimport { DataTable, DataTableRow } from '@/v2/components/data-table'\nimport { CollapsibleSection } from '@/v2/components/layout'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n clientOptions,\n eventBus,\n operation,\n method,\n path,\n selectedContentType,\n selectedServer = null,\n selectedExample,\n securitySchemes,\n selectedClient,\n globalCookies,\n openapiVersion,\n requestBodyCompositionSelection,\n integration,\n} = defineProps<CodeExampleProps & { eventBus: WorkspaceEventBus }>()\n\nconst { translate } = useLocalization()\n\n/** Grab any custom code samples from the operation */\nconst customCodeSamples = computed(() => getCustomCodeSamples(operation))\n\n/** Merge custom code samples with the client options */\nconst clients = computed(() =>\n getClients(\n customCodeSamples.value.samples,\n clientOptions,\n customCodeSamples.value.label,\n ),\n)\n\n/**\n * The locally selected client which would include code samples from this operation only\n * Must be local state because it contains custom code samples from this operation only\n */\nconst localSelectedClient = ref<ClientOption | CustomClientOption | undefined>(\n findClient(clients.value, selectedClient),\n)\n\n/**\n * Re-resolve the local client whenever the global selection or the available\n * clients change. Watching `clients` matters when navigating between operations:\n * the stored id stays the same, but the matching option (e.g. a custom sample)\n * differs per operation, so without this the snippet could go stale.\n */\nwatch([() => selectedClient, clients], ([newClient]) => {\n const client = findClient(clients.value, newClient)\n if (client) {\n localSelectedClient.value = client\n }\n})\n\n/** Block secrets from being shown in the code block */\nconst secretCredentials = computed(() => getSecrets(securitySchemes ?? []))\n\n/** Handle client change */\nconst handleClientChange = (option: ClientOption | undefined) => {\n localSelectedClient.value = option\n\n // Sync the selection globally (built-in client or custom sample keyed by language)\n if (option) {\n eventBus.emit('workspace:update:selected-client', option.id)\n }\n}\n\n/** Generate the code snippet for the selected example */\nconst generatedCode = computed<string | null>(() =>\n generateCodeSnippet({\n defaultDisabledParameters: true,\n clientId: localSelectedClient.value?.id,\n customCodeSamples: customCodeSamples.value.samples,\n operation,\n method,\n path,\n contentType: selectedContentType,\n server: selectedServer,\n securitySchemes,\n example: selectedExample,\n globalCookies,\n openapiVersion,\n requestBodyCompositionSelection,\n includeDefaultHeaders: integration === 'client',\n }),\n)\n\n/** Check if there are any clients available (built-in or custom code samples) */\nconst hasClients = computed(() =>\n clients.value.some((group) => group.options.length > 0),\n)\n</script>\n\n<template>\n <CollapsibleSection\n v-if=\"hasClients\"\n class=\"group/preview w-full border-t\"\n :defaultOpen=\"false\">\n <template #title>\n {{ translate('apiClient.requestCodeSnippet.codeSnippet') }}\n </template>\n\n <!-- Client selector -->\n <template #actions>\n <div class=\"flex flex-1\">\n <ScalarCombobox\n :modelValue=\"localSelectedClient\"\n :options=\"clients\"\n placement=\"bottom-end\"\n @update:modelValue=\"\n (ev) => handleClientChange(ev as ClientOption | undefined)\n \">\n <template #default=\"{ open }\">\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex h-full w-fit gap-1.5 px-0.5 py-0 text-base font-normal\"\n data-testid=\"client-picker\"\n variant=\"ghost\">\n {{ localSelectedClient?.title }}\n <ScalarIconCaretDown\n class=\"mt-0.25 size-3 transition-transform duration-100\"\n :class=\"open && 'rotate-180'\"\n weight=\"bold\" />\n </ScalarButton>\n </template>\n </ScalarCombobox>\n </div>\n </template>\n\n <!-- Code snippet -->\n <ScalarErrorBoundary>\n <DataTable\n :columns=\"['']\"\n presentational>\n <DataTableRow>\n <div class=\"overflow-hidden\">\n <div\n v-if=\"generatedCode === null\"\n class=\"text-c-2 p-4\"\n role=\"status\">\n {{ translate('apiClient.requestCodeSnippet.unavailable') }}\n </div>\n <ScalarCodeBlock\n v-else\n class=\"text-base\"\n :content=\"generatedCode\"\n :hideCredentials=\"secretCredentials\"\n :lang=\"localSelectedClient?.lang ?? 'plaintext'\"\n lineNumbers />\n </div>\n </DataTableRow>\n </DataTable>\n </ScalarErrorBoundary>\n </CollapsibleSection>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCA,MAAM,EAAE,cAAc,gBAAgB;;EAGtC,MAAM,oBAAoB,eAAe,qBAAqB,QAAA,SAAS,CAAC;;EAGxE,MAAM,UAAU,eACd,WACE,kBAAkB,MAAM,SACxB,QAAA,eACA,kBAAkB,MAAM,KAC1B,CACF;;;;;EAMA,MAAM,sBAAsB,IAC1B,WAAW,QAAQ,OAAO,QAAA,cAAc,CAC1C;;;;;;;EAQA,MAAM,OAAO,QAAA,gBAAgB,OAAO,IAAI,CAAC,eAAe;GACtD,MAAM,SAAS,WAAW,QAAQ,OAAO,SAAS;GAClD,IAAI,QACF,oBAAoB,QAAQ;EAEhC,CAAC;;EAGD,MAAM,oBAAoB,eAAe,WAAW,QAAA,mBAAmB,CAAC,CAAC,CAAC;;EAG1E,MAAM,sBAAsB,WAAqC;GAC/D,oBAAoB,QAAQ;GAG5B,IAAI,QACF,QAAA,SAAS,KAAK,oCAAoC,OAAO,EAAE;EAE/D;;EAGA,MAAM,gBAAgB,eACpB,oBAAoB;GAClB,2BAA2B;GAC3B,UAAU,oBAAoB,OAAO;GACrC,mBAAmB,kBAAkB,MAAM;GAC3C,WAAQ,QAAA;GACR,QAAK,QAAA;GACL,MAAG,QAAA;GACH,aAAa,QAAA;GACb,QAAQ,QAAA;GACR,iBAAc,QAAA;GACd,SAAS,QAAA;GACT,eAAY,QAAA;GACZ,gBAAa,QAAA;GACb,iCAA8B,QAAA;GAC9B,uBAAuB,QAAA,gBAAgB;EACzC,CAAC,CACH;;EAGA,MAAM,aAAa,eACjB,QAAQ,MAAM,MAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,CACxD;;UAKU,WAAA,SAAA,UAAA,GADR,YA0DqB,MAAA,0BAAA,GAAA;;IAxDnB,OAAM;IACL,aAAa;;IACH,OAAK,cAC6C,CAAA,gBAAA,gBAAxD,MAAA,SAAA,CAAS,CAAA,0CAAA,CAAA,GAAA,CAAA,CAAA,CAAA;IAIH,SAAO,cAsBV,CArBN,mBAqBM,OArBN,YAqBM,CApBJ,YAmBiB,MAAA,cAAA,GAAA;KAlBd,YAAY,oBAAA;KACZ,SAAS,QAAA;KACV,WAAU;KACT,uBAAiB,OAAA,OAAA,OAAA,MAAgB,OAAO,mBAAmB,EAAE;;KAGnD,SAAO,SAUD,EAVK,WAAI,CACxB,YASe,MAAA,YAAA,GAAA;MARb,OAAM;MACN,eAAY;MACZ,SAAQ;;6BACwB,CAAA,gBAAA,gBAA7B,oBAAA,OAAqB,KAAK,IAAG,KAChC,CAAA,GAAA,YAGkB,MAAA,mBAAA,GAAA;OAFhB,OAAK,eAAA,CAAC,oDACE,QAAI,YAAA,CAAA;OACZ,QAAO;;;;;;2BA8BG,CAtBtB,YAsBsB,MAAA,mBAAA,GAAA,MAAA;4BADR,CApBZ,YAoBY,MAAA,iBAAA,GAAA;MAnBT,SAAS,CAAA,EAAA;MACV,gBAAA;;6BAiBe,CAhBf,YAgBe,MAAA,oBAAA,GAAA,MAAA;8BADP,CAdN,mBAcM,OAdN,YAcM,CAZI,cAAA,UAAa,QAAA,UAAA,GADrB,mBAKM,OALN,YAKM,gBADD,MAAA,SAAA,CAAS,CAAA,0CAAA,CAAA,GAAA,CAAA,MAAA,UAAA,GAEd,YAMgB,MAAA,eAAA,GAAA;;QAJd,OAAM;QACL,SAAS,cAAA;QACT,iBAAiB,kBAAA;QACjB,MAAM,oBAAA,OAAqB,QAAI;QAChC,aAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestTable.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestTable.vue"],"names":[],"mappings":"AAwNA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AAGnH,OAAwB,EACtB,KAAK,QAAQ,EACb,KAAK,qBAAqB,EAC3B,MAAM,0DAA0D,CAAA;AAQjE,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAA;IAChB,uFAAuF;IACvF,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8BAA8B;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,WAAW,EAAE,kBAAkB,CAAA;CAChC,CAAC;AAuUF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"RequestTable.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/request-block/components/RequestTable.vue"],"names":[],"mappings":"AA4NA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2EAA2E,CAAA;AAGnH,OAAwB,EACtB,KAAK,QAAQ,EACb,KAAK,qBAAqB,EAC3B,MAAM,0DAA0D,CAAA;AAQjE,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAA;IAChB,uFAAuF;IACvF,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,8BAA8B;IAC9B,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,WAAW,EAAE,kBAAkB,CAAA;CAChC,CAAC;AA2UF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_
2
2
  import RequestTable_vue_vue_type_script_setup_true_lang_default from "./RequestTable.vue.script.js";
3
3
  /* empty css */
4
4
  //#region src/v2/blocks/request-block/components/RequestTable.vue
5
- var RequestTable_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RequestTable_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-bb7a09e9"]]);
5
+ var RequestTable_default = /*#__PURE__*/ _plugin_vue_export_helper_default(RequestTable_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e29008b7"]]);
6
6
  //#endregion
7
7
  export { RequestTable_default as default };
8
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"RequestTable.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { computed } from 'vue'\n\nimport RequestTableRow, {\n type TableRow,\n type TableRowUpsertPayload,\n} from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n data,\n hasCheckboxDisabled,\n deferKeyUpdates,\n showUploadButton,\n showAddRowPlaceholder = true,\n environment,\n} = defineProps<{\n data: TableRow[]\n /** Save key edits on blur so changing a body name does not replace the focused row. */\n deferKeyUpdates?: boolean\n /** Hide the enabled column */\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n showUploadButton?: boolean\n showAddRowPlaceholder?: boolean\n environment: XScalarEnvironment\n}>()\n\n/**\n * Make this component more generic that can be used also for the operation body\n */\nconst emit = defineEmits<{\n (e: 'upsertRow', index: number, payload: TableRowUpsertPayload): void\n (e: 'deleteRow', index: number): void\n\n /**\n * File upload events\n *\n * Each row has its own upload button, so we need to know which row to update\n * when the file is selected.\n */\n (e: 'uploadFile', index: number): void\n (e: 'removeFile', index: number): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n /** Select a value for a grouped global cookie preset at the given row index. */\n (e: 'selectPreset', index: number, value: string): void\n}>()\n\nconst { translate } = useLocalization()\n\nconst columns = computed(() => {\n if (showUploadButton) {\n return ['36px', '', '', 'minmax(0, 1fr)']\n }\n return ['36px', '', '']\n})\n\n/** Add the last empty row (for ui purposes only) */\nconst displayData = computed(() => {\n if (!showAddRowPlaceholder) {\n return data\n }\n\n const last = data.at(-1)\n\n if (!last || last.name !== '' || last.value !== '') {\n return [...data, { name: '', value: '', isDisabled: true }]\n }\n\n return data\n})\n\n/**\n * Stable identity key for each row so Vue never reuses a RequestTableRow instance for a different\n * parameter or the appended placeholder row. Parameter rows are keyed by their parameter identity —\n * the name plus the value path for expanded object parameters. The parts are combined through\n * JSON.stringify so the name/path boundary is unambiguous (for example `ab` + `['c']` never\n * collides with `a` + `['bc']`). Form rows use the name and its occurrence so repeated\n * multipart fields remain distinct while unrelated rows can move without losing their identity.\n */\nconst getRowKey = (row: TableRow, index: number): string => {\n if (row.originalParameter) {\n return JSON.stringify([\n row.originalParameter.name,\n ...(row.sourceParameterValuePath ?? []),\n ])\n }\n\n const occurrence = displayData.value\n .slice(0, index)\n .filter(\n (other) => !other.originalParameter && other.name === row.name,\n ).length\n return `row:${JSON.stringify([row.name, occurrence])}`\n}\n\n/** Keep an edited row mounted when saving it changes its parameter identity. */\ntype DisplayRow = {\n data: TableRow\n identity: string\n key: symbol\n}\n\nconst pendingUpdates = new Map<symbol, TableRowUpsertPayload>()\n\nconst matchesPendingUpdate = (key: symbol, row: TableRow): boolean => {\n const update = pendingUpdates.get(key)\n return update?.name === row.name && update.value === row.value\n}\n\n// A saved parameter inherits the editor's key. The next placeholder gets a fresh key,\n// so it cannot retain the previous placeholder's text or focused input.\nconst keyedRows = computed<DisplayRow[]>((previous = []) => {\n const available = new Set(previous)\n // Reserve identities still present in the store before transferring an edited key.\n // The appended placeholder is excluded so a newly saved row can inherit its editor.\n const savedIdentities = new Set(data.map(getRowKey))\n const rows = displayData.value.map((row, index) => {\n const identity = getRowKey(row, index)\n const existing = [...available].find((entry) => entry.identity === identity)\n const pending = [...available].find(\n (entry) =>\n !savedIdentities.has(entry.identity) &&\n !entry.data.sourceParameterValuePath &&\n matchesPendingUpdate(entry.key, row),\n )\n const match = existing ?? pending\n\n if (match) {\n available.delete(match)\n if (matchesPendingUpdate(match.key, row)) {\n pendingUpdates.delete(match.key)\n }\n }\n\n return { data: row, identity, key: match?.key ?? Symbol() }\n })\n\n for (const removed of available) {\n pendingUpdates.delete(removed.key)\n }\n\n return rows\n})\n\nconst handleUpsertRow = (\n row: DisplayRow,\n index: number,\n payload: TableRowUpsertPayload,\n): void => {\n // The store may debounce the save. Transfer this key only when the saved row arrives.\n pendingUpdates.set(row.key, payload)\n emit('upsertRow', index, payload)\n}\n</script>\n<template>\n <DataTable\n class=\"group/table flex-1\"\n :columns=\"columns\">\n <DataTableRow class=\"sr-only !block\">\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.enabled') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.key') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.value') }}\n </DataTableHeader>\n </DataTableRow>\n\n <RequestTableRow\n v-for=\"(row, index) in keyedRows\"\n :key=\"row.key\"\n :data=\"row.data\"\n :deferKeyUpdates=\"deferKeyUpdates\"\n :environment=\"environment\"\n :hasCheckboxDisabled=\"hasCheckboxDisabled\"\n :invalidParams=\"invalidParams\"\n :label=\"label\"\n :showUploadButton=\"showUploadButton\"\n @deleteRow=\"emit('deleteRow', index)\"\n @navigate=\"(route) => emit('navigate', route)\"\n @removeFile=\"emit('removeFile', index)\"\n @selectPreset=\"(value) => emit('selectPreset', index, value)\"\n @uploadFile=\"emit('uploadFile', index)\"\n @upsertRow=\"(payload) => handleUpsertRow(row, index, payload)\" />\n </DataTable>\n</template>\n<style scoped>\n:deep(.code-input-lite__editor) {\n background-color: transparent;\n font-family: var(--scalar-font);\n font-size: var(--scalar-small);\n padding: 5px 8px;\n}\n:deep(.scalar-pill:not(:last-of-type)) {\n margin-right: 0.15em;\n}\n:deep(.scalar-pill:not(:first-of-type)) {\n margin-left: 0.5px;\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"RequestTable.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { computed } from 'vue'\n\nimport RequestTableRow, {\n type TableRow,\n type TableRowUpsertPayload,\n} from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n data,\n hasCheckboxDisabled,\n deferKeyUpdates,\n showUploadButton,\n showAddRowPlaceholder = true,\n environment,\n} = defineProps<{\n data: TableRow[]\n /** Save key edits on blur so changing a body name does not replace the focused row. */\n deferKeyUpdates?: boolean\n /** Hide the enabled column */\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n showUploadButton?: boolean\n showAddRowPlaceholder?: boolean\n environment: XScalarEnvironment\n}>()\n\n/**\n * Make this component more generic that can be used also for the operation body\n */\nconst emit = defineEmits<{\n (e: 'upsertRow', index: number, payload: TableRowUpsertPayload): void\n (e: 'deleteRow', index: number): void\n\n /**\n * File upload events\n *\n * Each row has its own upload button, so we need to know which row to update\n * when the file is selected.\n */\n (e: 'uploadFile', index: number): void\n (e: 'removeFile', index: number): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n /** Select a value for a grouped global cookie preset at the given row index. */\n (e: 'selectPreset', index: number, value: string): void\n}>()\n\nconst { translate } = useLocalization()\n\nconst columns = computed(() => {\n if (showUploadButton) {\n return ['36px', '', '', 'minmax(0, 1fr)']\n }\n return ['36px', '', '']\n})\n\n/** Add the last empty row (for ui purposes only) */\nconst displayData = computed(() => {\n if (!showAddRowPlaceholder) {\n return data\n }\n\n const last = data.at(-1)\n\n if (!last || last.name !== '' || last.value !== '') {\n return [...data, { name: '', value: '', isDisabled: true }]\n }\n\n return data\n})\n\n/**\n * Stable identity key for each row so Vue never reuses a RequestTableRow instance for a different\n * parameter or the appended placeholder row. Parameter rows are keyed by their parameter identity —\n * the name plus the value path for expanded object parameters. The parts are combined through\n * JSON.stringify so the name/path boundary is unambiguous (for example `ab` + `['c']` never\n * collides with `a` + `['bc']`). Form rows use the name and its occurrence so repeated\n * multipart fields remain distinct while unrelated rows can move without losing their identity.\n */\nconst getRowKey = (row: TableRow, index: number): string => {\n // Body edits retain their row when blur commits a rename after focus moves into the value.\n if (deferKeyUpdates) {\n return `body:${index}`\n }\n if (row.originalParameter) {\n return JSON.stringify([\n row.originalParameter.name,\n ...(row.sourceParameterValuePath ?? []),\n ])\n }\n\n const occurrence = displayData.value\n .slice(0, index)\n .filter(\n (other) => !other.originalParameter && other.name === row.name,\n ).length\n return `row:${JSON.stringify([row.name, occurrence])}`\n}\n\n/** Keep an edited row mounted when saving it changes its parameter identity. */\ntype DisplayRow = {\n data: TableRow\n identity: string\n key: symbol\n}\n\nconst pendingUpdates = new Map<symbol, TableRowUpsertPayload>()\n\nconst matchesPendingUpdate = (key: symbol, row: TableRow): boolean => {\n const update = pendingUpdates.get(key)\n return update?.name === row.name && update.value === row.value\n}\n\n// A saved parameter inherits the editor's key. The next placeholder gets a fresh key,\n// so it cannot retain the previous placeholder's text or focused input.\nconst keyedRows = computed<DisplayRow[]>((previous = []) => {\n const available = new Set(previous)\n // Reserve identities still present in the store before transferring an edited key.\n // The appended placeholder is excluded so a newly saved row can inherit its editor.\n const savedIdentities = new Set(data.map(getRowKey))\n const rows = displayData.value.map((row, index) => {\n const identity = getRowKey(row, index)\n const existing = [...available].find((entry) => entry.identity === identity)\n const pending = [...available].find(\n (entry) =>\n !savedIdentities.has(entry.identity) &&\n !entry.data.sourceParameterValuePath &&\n matchesPendingUpdate(entry.key, row),\n )\n const match = existing ?? pending\n\n if (match) {\n available.delete(match)\n if (matchesPendingUpdate(match.key, row)) {\n pendingUpdates.delete(match.key)\n }\n }\n\n return { data: row, identity, key: match?.key ?? Symbol() }\n })\n\n for (const removed of available) {\n pendingUpdates.delete(removed.key)\n }\n\n return rows\n})\n\nconst handleUpsertRow = (\n row: DisplayRow,\n index: number,\n payload: TableRowUpsertPayload,\n): void => {\n // The store may debounce the save. Transfer this key only when the saved row arrives.\n pendingUpdates.set(row.key, payload)\n emit('upsertRow', index, payload)\n}\n</script>\n<template>\n <DataTable\n class=\"group/table flex-1\"\n :columns=\"columns\">\n <DataTableRow class=\"sr-only !block\">\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.enabled') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.key') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.value') }}\n </DataTableHeader>\n </DataTableRow>\n\n <RequestTableRow\n v-for=\"(row, index) in keyedRows\"\n :key=\"row.key\"\n :data=\"row.data\"\n :deferKeyUpdates=\"deferKeyUpdates\"\n :environment=\"environment\"\n :hasCheckboxDisabled=\"hasCheckboxDisabled\"\n :invalidParams=\"invalidParams\"\n :label=\"label\"\n :showUploadButton=\"showUploadButton\"\n @deleteRow=\"emit('deleteRow', index)\"\n @navigate=\"(route) => emit('navigate', route)\"\n @removeFile=\"emit('removeFile', index)\"\n @selectPreset=\"(value) => emit('selectPreset', index, value)\"\n @uploadFile=\"emit('uploadFile', index)\"\n @upsertRow=\"(payload) => handleUpsertRow(row, index, payload)\" />\n </DataTable>\n</template>\n<style scoped>\n:deep(.code-input-lite__editor) {\n background-color: transparent;\n font-family: var(--scalar-font);\n font-size: var(--scalar-small);\n padding: 5px 8px;\n}\n:deep(.scalar-pill:not(:last-of-type)) {\n margin-right: 0.15em;\n}\n:deep(.scalar-pill:not(:first-of-type)) {\n margin-left: 0.5px;\n}\n</style>\n"],"mappings":""}
@@ -67,6 +67,7 @@ var RequestTable_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
67
67
  * multipart fields remain distinct while unrelated rows can move without losing their identity.
68
68
  */
69
69
  const getRowKey = (row, index) => {
70
+ if (__props.deferKeyUpdates) return `body:${index}`;
70
71
  if (row.originalParameter) return JSON.stringify([row.originalParameter.name, ...row.sourceParameterValuePath ?? []]);
71
72
  const occurrence = displayData.value.slice(0, index).filter((other) => !other.originalParameter && other.name === row.name).length;
72
73
  return `row:${JSON.stringify([row.name, occurrence])}`;
@@ -1 +1 @@
1
- {"version":3,"file":"RequestTable.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { computed } from 'vue'\n\nimport RequestTableRow, {\n type TableRow,\n type TableRowUpsertPayload,\n} from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n data,\n hasCheckboxDisabled,\n deferKeyUpdates,\n showUploadButton,\n showAddRowPlaceholder = true,\n environment,\n} = defineProps<{\n data: TableRow[]\n /** Save key edits on blur so changing a body name does not replace the focused row. */\n deferKeyUpdates?: boolean\n /** Hide the enabled column */\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n showUploadButton?: boolean\n showAddRowPlaceholder?: boolean\n environment: XScalarEnvironment\n}>()\n\n/**\n * Make this component more generic that can be used also for the operation body\n */\nconst emit = defineEmits<{\n (e: 'upsertRow', index: number, payload: TableRowUpsertPayload): void\n (e: 'deleteRow', index: number): void\n\n /**\n * File upload events\n *\n * Each row has its own upload button, so we need to know which row to update\n * when the file is selected.\n */\n (e: 'uploadFile', index: number): void\n (e: 'removeFile', index: number): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n /** Select a value for a grouped global cookie preset at the given row index. */\n (e: 'selectPreset', index: number, value: string): void\n}>()\n\nconst { translate } = useLocalization()\n\nconst columns = computed(() => {\n if (showUploadButton) {\n return ['36px', '', '', 'minmax(0, 1fr)']\n }\n return ['36px', '', '']\n})\n\n/** Add the last empty row (for ui purposes only) */\nconst displayData = computed(() => {\n if (!showAddRowPlaceholder) {\n return data\n }\n\n const last = data.at(-1)\n\n if (!last || last.name !== '' || last.value !== '') {\n return [...data, { name: '', value: '', isDisabled: true }]\n }\n\n return data\n})\n\n/**\n * Stable identity key for each row so Vue never reuses a RequestTableRow instance for a different\n * parameter or the appended placeholder row. Parameter rows are keyed by their parameter identity —\n * the name plus the value path for expanded object parameters. The parts are combined through\n * JSON.stringify so the name/path boundary is unambiguous (for example `ab` + `['c']` never\n * collides with `a` + `['bc']`). Form rows use the name and its occurrence so repeated\n * multipart fields remain distinct while unrelated rows can move without losing their identity.\n */\nconst getRowKey = (row: TableRow, index: number): string => {\n if (row.originalParameter) {\n return JSON.stringify([\n row.originalParameter.name,\n ...(row.sourceParameterValuePath ?? []),\n ])\n }\n\n const occurrence = displayData.value\n .slice(0, index)\n .filter(\n (other) => !other.originalParameter && other.name === row.name,\n ).length\n return `row:${JSON.stringify([row.name, occurrence])}`\n}\n\n/** Keep an edited row mounted when saving it changes its parameter identity. */\ntype DisplayRow = {\n data: TableRow\n identity: string\n key: symbol\n}\n\nconst pendingUpdates = new Map<symbol, TableRowUpsertPayload>()\n\nconst matchesPendingUpdate = (key: symbol, row: TableRow): boolean => {\n const update = pendingUpdates.get(key)\n return update?.name === row.name && update.value === row.value\n}\n\n// A saved parameter inherits the editor's key. The next placeholder gets a fresh key,\n// so it cannot retain the previous placeholder's text or focused input.\nconst keyedRows = computed<DisplayRow[]>((previous = []) => {\n const available = new Set(previous)\n // Reserve identities still present in the store before transferring an edited key.\n // The appended placeholder is excluded so a newly saved row can inherit its editor.\n const savedIdentities = new Set(data.map(getRowKey))\n const rows = displayData.value.map((row, index) => {\n const identity = getRowKey(row, index)\n const existing = [...available].find((entry) => entry.identity === identity)\n const pending = [...available].find(\n (entry) =>\n !savedIdentities.has(entry.identity) &&\n !entry.data.sourceParameterValuePath &&\n matchesPendingUpdate(entry.key, row),\n )\n const match = existing ?? pending\n\n if (match) {\n available.delete(match)\n if (matchesPendingUpdate(match.key, row)) {\n pendingUpdates.delete(match.key)\n }\n }\n\n return { data: row, identity, key: match?.key ?? Symbol() }\n })\n\n for (const removed of available) {\n pendingUpdates.delete(removed.key)\n }\n\n return rows\n})\n\nconst handleUpsertRow = (\n row: DisplayRow,\n index: number,\n payload: TableRowUpsertPayload,\n): void => {\n // The store may debounce the save. Transfer this key only when the saved row arrives.\n pendingUpdates.set(row.key, payload)\n emit('upsertRow', index, payload)\n}\n</script>\n<template>\n <DataTable\n class=\"group/table flex-1\"\n :columns=\"columns\">\n <DataTableRow class=\"sr-only !block\">\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.enabled') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.key') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.value') }}\n </DataTableHeader>\n </DataTableRow>\n\n <RequestTableRow\n v-for=\"(row, index) in keyedRows\"\n :key=\"row.key\"\n :data=\"row.data\"\n :deferKeyUpdates=\"deferKeyUpdates\"\n :environment=\"environment\"\n :hasCheckboxDisabled=\"hasCheckboxDisabled\"\n :invalidParams=\"invalidParams\"\n :label=\"label\"\n :showUploadButton=\"showUploadButton\"\n @deleteRow=\"emit('deleteRow', index)\"\n @navigate=\"(route) => emit('navigate', route)\"\n @removeFile=\"emit('removeFile', index)\"\n @selectPreset=\"(value) => emit('selectPreset', index, value)\"\n @uploadFile=\"emit('uploadFile', index)\"\n @upsertRow=\"(payload) => handleUpsertRow(row, index, payload)\" />\n </DataTable>\n</template>\n<style scoped>\n:deep(.code-input-lite__editor) {\n background-color: transparent;\n font-family: var(--scalar-font);\n font-size: var(--scalar-small);\n padding: 5px 8px;\n}\n:deep(.scalar-pill:not(:last-of-type)) {\n margin-right: 0.15em;\n}\n:deep(.scalar-pill:not(:first-of-type)) {\n margin-left: 0.5px;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RequestTable.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport { computed } from 'vue'\n\nimport RequestTableRow, {\n type TableRow,\n type TableRowUpsertPayload,\n} from '@/v2/blocks/request-block/components/RequestTableRow.vue'\nimport {\n DataTable,\n DataTableHeader,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nconst {\n data,\n hasCheckboxDisabled,\n deferKeyUpdates,\n showUploadButton,\n showAddRowPlaceholder = true,\n environment,\n} = defineProps<{\n data: TableRow[]\n /** Save key edits on blur so changing a body name does not replace the focused row. */\n deferKeyUpdates?: boolean\n /** Hide the enabled column */\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n showUploadButton?: boolean\n showAddRowPlaceholder?: boolean\n environment: XScalarEnvironment\n}>()\n\n/**\n * Make this component more generic that can be used also for the operation body\n */\nconst emit = defineEmits<{\n (e: 'upsertRow', index: number, payload: TableRowUpsertPayload): void\n (e: 'deleteRow', index: number): void\n\n /**\n * File upload events\n *\n * Each row has its own upload button, so we need to know which row to update\n * when the file is selected.\n */\n (e: 'uploadFile', index: number): void\n (e: 'removeFile', index: number): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n /** Select a value for a grouped global cookie preset at the given row index. */\n (e: 'selectPreset', index: number, value: string): void\n}>()\n\nconst { translate } = useLocalization()\n\nconst columns = computed(() => {\n if (showUploadButton) {\n return ['36px', '', '', 'minmax(0, 1fr)']\n }\n return ['36px', '', '']\n})\n\n/** Add the last empty row (for ui purposes only) */\nconst displayData = computed(() => {\n if (!showAddRowPlaceholder) {\n return data\n }\n\n const last = data.at(-1)\n\n if (!last || last.name !== '' || last.value !== '') {\n return [...data, { name: '', value: '', isDisabled: true }]\n }\n\n return data\n})\n\n/**\n * Stable identity key for each row so Vue never reuses a RequestTableRow instance for a different\n * parameter or the appended placeholder row. Parameter rows are keyed by their parameter identity —\n * the name plus the value path for expanded object parameters. The parts are combined through\n * JSON.stringify so the name/path boundary is unambiguous (for example `ab` + `['c']` never\n * collides with `a` + `['bc']`). Form rows use the name and its occurrence so repeated\n * multipart fields remain distinct while unrelated rows can move without losing their identity.\n */\nconst getRowKey = (row: TableRow, index: number): string => {\n // Body edits retain their row when blur commits a rename after focus moves into the value.\n if (deferKeyUpdates) {\n return `body:${index}`\n }\n if (row.originalParameter) {\n return JSON.stringify([\n row.originalParameter.name,\n ...(row.sourceParameterValuePath ?? []),\n ])\n }\n\n const occurrence = displayData.value\n .slice(0, index)\n .filter(\n (other) => !other.originalParameter && other.name === row.name,\n ).length\n return `row:${JSON.stringify([row.name, occurrence])}`\n}\n\n/** Keep an edited row mounted when saving it changes its parameter identity. */\ntype DisplayRow = {\n data: TableRow\n identity: string\n key: symbol\n}\n\nconst pendingUpdates = new Map<symbol, TableRowUpsertPayload>()\n\nconst matchesPendingUpdate = (key: symbol, row: TableRow): boolean => {\n const update = pendingUpdates.get(key)\n return update?.name === row.name && update.value === row.value\n}\n\n// A saved parameter inherits the editor's key. The next placeholder gets a fresh key,\n// so it cannot retain the previous placeholder's text or focused input.\nconst keyedRows = computed<DisplayRow[]>((previous = []) => {\n const available = new Set(previous)\n // Reserve identities still present in the store before transferring an edited key.\n // The appended placeholder is excluded so a newly saved row can inherit its editor.\n const savedIdentities = new Set(data.map(getRowKey))\n const rows = displayData.value.map((row, index) => {\n const identity = getRowKey(row, index)\n const existing = [...available].find((entry) => entry.identity === identity)\n const pending = [...available].find(\n (entry) =>\n !savedIdentities.has(entry.identity) &&\n !entry.data.sourceParameterValuePath &&\n matchesPendingUpdate(entry.key, row),\n )\n const match = existing ?? pending\n\n if (match) {\n available.delete(match)\n if (matchesPendingUpdate(match.key, row)) {\n pendingUpdates.delete(match.key)\n }\n }\n\n return { data: row, identity, key: match?.key ?? Symbol() }\n })\n\n for (const removed of available) {\n pendingUpdates.delete(removed.key)\n }\n\n return rows\n})\n\nconst handleUpsertRow = (\n row: DisplayRow,\n index: number,\n payload: TableRowUpsertPayload,\n): void => {\n // The store may debounce the save. Transfer this key only when the saved row arrives.\n pendingUpdates.set(row.key, payload)\n emit('upsertRow', index, payload)\n}\n</script>\n<template>\n <DataTable\n class=\"group/table flex-1\"\n :columns=\"columns\">\n <DataTableRow class=\"sr-only !block\">\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.enabled') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.key') }}\n </DataTableHeader>\n <DataTableHeader>\n {{ label }}\n {{ translate('apiClient.requestTable.value') }}\n </DataTableHeader>\n </DataTableRow>\n\n <RequestTableRow\n v-for=\"(row, index) in keyedRows\"\n :key=\"row.key\"\n :data=\"row.data\"\n :deferKeyUpdates=\"deferKeyUpdates\"\n :environment=\"environment\"\n :hasCheckboxDisabled=\"hasCheckboxDisabled\"\n :invalidParams=\"invalidParams\"\n :label=\"label\"\n :showUploadButton=\"showUploadButton\"\n @deleteRow=\"emit('deleteRow', index)\"\n @navigate=\"(route) => emit('navigate', route)\"\n @removeFile=\"emit('removeFile', index)\"\n @selectPreset=\"(value) => emit('selectPreset', index, value)\"\n @uploadFile=\"emit('uploadFile', index)\"\n @upsertRow=\"(payload) => handleUpsertRow(row, index, payload)\" />\n </DataTable>\n</template>\n<style scoped>\n:deep(.code-input-lite__editor) {\n background-color: transparent;\n font-family: var(--scalar-font);\n font-size: var(--scalar-small);\n padding: 5px 8px;\n}\n:deep(.scalar-pill:not(:last-of-type)) {\n margin-right: 0.15em;\n}\n:deep(.scalar-pill:not(:first-of-type)) {\n margin-left: 0.5px;\n}\n</style>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestTableRow.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableRow.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarIconGlobe, ScalarIconTrash } from '@scalar/icons'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ParameterObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { computed, ref, watch } from 'vue'\n\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\nimport { CodeInputLite } from '@/v2/components/code-input'\nimport {\n DataTableCell,\n DataTableCheckbox,\n DataTableInputSelect,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport RequestTableTooltip from './RequestTableTooltip.vue'\n\nconst {\n data,\n environment,\n hasCheckboxDisabled,\n deferKeyUpdates,\n invalidParams,\n showUploadButton,\n} = defineProps<{\n data: TableRow\n /** Keep key edits local until blur when the row identity depends on its name. */\n deferKeyUpdates?: boolean\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n environment: XScalarEnvironment\n showUploadButton?: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: 'upsertRow', payload: TableRowUpsertPayload): void\n (e: 'deleteRow'): void\n (e: 'uploadFile'): void\n (e: 'removeFile'): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n /** Select a value for a grouped global cookie preset. */\n (e: 'selectPreset', value: string): void\n}>()\n\nconst { translate } = useLocalization()\n\nexport type TableRow = {\n /** The parameter or field name/key */\n name: string\n /** The parameter value, can be a string, file, or null */\n value: string | File | null\n /** Optional description for the parameter */\n description?: string\n /** Optional route for global parameters (e.g., cookies shared across workspace) */\n globalRoute?: ApiReferenceEvents['ui:navigate']\n /** Whether the parameter is disabled/inactive */\n isDisabled?: boolean\n /** Whether an optional parameter is disabled because it has no explicit x-disabled value */\n isDisabledByDefault?: boolean\n /** OpenAPI schema object with type, validation rules, examples, etc. */\n schema?: SchemaObject\n /** Preserve array values even while their JSON text is temporarily invalid. */\n isArray?: boolean\n /** Whether the parameter is required */\n isRequired?: boolean\n /**\n * Whether the parameter is readonly and can not be modifies directly\n * User can still override the parameter which is going to show up with the linethrough style\n */\n isReadonly?: boolean\n /** Whether the parameter is overridden later on */\n isOverridden?: boolean\n /** Track the original parameter so we can update it */\n originalParameter?: ParameterObject\n /** Path to a value inside the original parameter example for expanded object parameters */\n sourceParameterValuePath?: string[]\n /**\n * Selectable values for a grouped global cookie preset. When set, the value cell renders a\n * dropdown to switch between predefined values (for example a `Culture` cookie with `PL`/`EN`)\n * instead of an editable input.\n */\n presetOptions?: string[]\n}\n\nexport type TableRowUpsertPayload = {\n name: string\n value: string | File\n isDisabled: boolean\n shouldRenameExpandedRow?: boolean\n}\n\n/**\n * Track local state for the row\n *\n * Now this is required because of the way we get default values from the schema.\n * If we have a default value in data.value, then we update isDisabled to true. We lose the default value since now\n * we have an example where value: ''. This is why we need to track the local state and update all of the params at the\n * same time.\n */\nconst name = ref<string>(data.name ?? '')\nconst value = ref<string | File>(unpackProxyObject(data.value) ?? '')\nconst isDisabled = ref<boolean>(data.isDisabled ?? false)\n\n// Keep the above state synced with the data prop\nwatch(\n () => data.name,\n (newName) => {\n // Do not overwrite a valid local name with an empty incoming prop — this\n // can happen when the placeholder row (appended by displayData) is briefly\n // mapped onto this component instance before Vue re-keys the list.\n if (!newName && name.value) return\n name.value = newName ?? ''\n },\n)\nwatch(\n () => data.value,\n (newValue) => (value.value = unpackProxyObject(newValue) ?? ''),\n)\nwatch(\n () => data.isDisabled,\n (newIsDisabled) => (isDisabled.value = newIsDisabled ?? false),\n)\n\n/** Check if the value is a File instance */\nconst isFile = computed(() => value.value instanceof File)\n\n/** Display value handles File instances and shows filename instead */\nconst displayValue = computed(\n () =>\n (isFile.value\n ? getFileName(value.value as File)\n : (value.value as string)) ?? '',\n)\n\nconst defaultValue = computed(() => data.schema?.default as string)\n\n/** See if we can extract enum values from the schema */\nconst enumValue = computed<string[]>(() => {\n if (!data.schema) {\n return []\n }\n\n // Grab the enum from the schema\n if (data.schema.enum) {\n return data.schema.enum.map((item) => String(item))\n }\n\n // Grab the enum from the items schema\n if ('items' in data.schema) {\n const resolved = resolve.schema(data.schema.items)\n if (resolved?.enum) {\n return resolved.enum.map((item) => String(item))\n }\n }\n\n return []\n})\n\nconst typeValue = computed(() =>\n data.schema && 'type' in data.schema ? data.schema.type : undefined,\n)\n\nconst validationResult = computed(() =>\n validateParameter(data.schema, value.value),\n)\n\n/** Handle row updates while preserving existing properties */\nconst handleUpdateRow = (\n payload: Partial<{ name: string; value: string; isDisabled: boolean }>,\n options: { shouldRenameExpandedRow?: boolean; commitKey?: boolean } = {},\n): void => {\n // Update our local state\n if (payload.name !== undefined) {\n name.value = payload.name\n }\n if (payload.value !== undefined) {\n value.value = payload.value\n\n // Optional parameters start unchecked, but entering a value expresses intent to send it.\n // Explicit x-disabled values stay unchanged so a deliberate opt-out is preserved.\n if (data.isDisabledByDefault) {\n isDisabled.value = false\n }\n }\n\n // Is disabled should only be updated when explicitly provided in the payload\n if (payload.isDisabled !== undefined) {\n isDisabled.value = payload.isDisabled\n }\n\n if (\n payload.name !== undefined &&\n (deferKeyUpdates || data.sourceParameterValuePath) &&\n !options.shouldRenameExpandedRow &&\n !options.commitKey\n ) {\n return\n }\n\n // Emit all of the local state\n emit('upsertRow', {\n name: name.value,\n value: value.value,\n isDisabled: isDisabled.value,\n ...(options.shouldRenameExpandedRow\n ? { shouldRenameExpandedRow: true }\n : {}),\n })\n}\n\n/**\n * Commit a key edit when the input loses focus. Body and expanded-object rows defer their rename to blur (see\n * handleUpdateRow), so we only emit when the key actually changed. Focusing and blurring the field\n * without typing should not re-emit the row or silently reset its disabled state. The current\n * disabled state is passed through so a renamed row keeps it.\n */\nconst handleKeyBlur = (newName: string): void => {\n if (newName === data.name) {\n return\n }\n // Do not emit an update that would blank the parameter name — this can fire\n // when CodeInputLite blurs before it has rendered its initial value.\n if (!newName && data.name) {\n return\n }\n\n handleUpdateRow(\n { name: newName, isDisabled: isDisabled.value },\n {\n shouldRenameExpandedRow: Boolean(data.sourceParameterValuePath),\n commitKey: true,\n },\n )\n}\n\n/** Save a pending body key before the send shortcut reaches the request handler. */\nconst handleKeydown = (event: KeyboardEvent): void => {\n if (\n deferKeyUpdates &&\n event.key === 'Enter' &&\n (event.metaKey || event.ctrlKey)\n ) {\n handleKeyBlur(name.value)\n }\n}\n</script>\n\n<template>\n <DataTableRow\n :id=\"data.name\"\n :class=\"{\n alert: validationResult.ok === false,\n error: validationResult.ok === false && invalidParams?.has(data.name),\n }\">\n <DataTableCheckbox\n :ariaLabel=\"\n translate('apiClient.requestTableRow.include', {\n name: data.name || translate('apiClient.requestTableRow.row'),\n })\n \"\n class=\"!border-r\"\n :disabled=\"hasCheckboxDisabled ?? false\"\n :modelValue=\"!isDisabled\"\n @update:modelValue=\"(v) => handleUpdateRow({ isDisabled: !v })\" />\n\n <!-- Name -->\n <DataTableCell>\n <CodeInputLite\n :aria-label=\"\n translate('apiClient.requestTableRow.keyLabel', { name: label ?? '' })\n \"\n :disabled=\"data.isReadonly\"\n :environment=\"environment\"\n :modelValue=\"name\"\n :placeholder=\"translate('apiClient.requestTableRow.key')\"\n :required=\"Boolean(data.isRequired)\"\n @blur=\"(v) => handleKeyBlur(v)\"\n @keydown.capture=\"handleKeydown\"\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ name: v })\" />\n </DataTableCell>\n\n <!-- Value -->\n <DataTableCell>\n <!-- Preset switcher for grouped global cookies that share a name -->\n <DataTableInputSelect\n v-if=\"data.presetOptions?.length\"\n :canAddCustomValue=\"false\"\n :modelValue=\"displayValue\"\n :value=\"data.presetOptions\"\n @update:modelValue=\"(v) => emit('selectPreset', v)\" />\n <CodeInputLite\n v-else\n :aria-label=\"\n translate('apiClient.requestTableRow.valueLabel', {\n name: label ?? '',\n })\n \"\n class=\"pr-6 group-hover:pr-10 group-has-[.code-input-lite__editor:focus]:pr-10\"\n :default=\"defaultValue\"\n :disabled=\"data.isReadonly\"\n :enum=\"enumValue\"\n :environment=\"environment\"\n :examples=\"\n data.schema?.examples?.map((example) => String(example)) ?? []\n \"\n :linethrough=\"data.isOverridden\"\n :modelValue=\"displayValue\"\n :placeholder=\"translate('apiClient.requestTableRow.value')\"\n :type=\"typeValue\"\n withFakeData\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ value: v })\">\n <template #icon>\n <ScalarButton\n v-if=\"\n Boolean(data.name || value) &&\n !data.isRequired &&\n data.isReadonly !== true\n \"\n :aria-label=\"\n translate('apiClient.requestTableRow.deleteRow', {\n name: data.name || translate('apiClient.requestTableRow.row'),\n })\n \"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 hidden h-fit rounded p-1 group-hover:flex group-has-[.code-input-lite__editor:focus]:flex\"\n size=\"sm\"\n variant=\"ghost\"\n @click=\"emit('deleteRow')\">\n <ScalarIconTrash class=\"size-3.5\" />\n </ScalarButton>\n\n <ScalarIconButton\n v-if=\"data.globalRoute !== undefined\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 h-fit\"\n :icon=\"ScalarIconGlobe\"\n :label=\"translate('apiClient.requestTableRow.globalCookieHint')\"\n size=\"xs\"\n tooltip=\"top\"\n variant=\"ghost\"\n @click=\"emit('navigate', data.globalRoute)\" />\n\n <RequestTableTooltip\n v-if=\"data.isReadonly\"\n :description=\"translate('apiClient.requestTableRow.readOnlyHint')\"\n :value=\"null\" />\n <RequestTableTooltip\n v-else-if=\"data.schema\"\n :description=\"data.description\"\n :schema=\"data.schema\"\n :value />\n </template>\n </CodeInputLite>\n </DataTableCell>\n\n <!-- File upload -->\n <DataTableCell\n v-if=\"showUploadButton\"\n class=\"group/upload flex items-center justify-center whitespace-nowrap\">\n <template v-if=\"isFile\">\n <div\n class=\"text-c-2 flex w-full max-w-[100%] items-center justify-center overflow-hidden p-1\">\n <span\n class=\"truncate\"\n :title=\"displayValue\"\n >{{ displayValue }}</span\n >\n </div>\n <button\n class=\"bg-b-2 mt-1 block rounded p-0.5 text-center text-xs font-medium md:pointer-events-none md:absolute md:inset-x-1 md:top-1/2 md:mt-0 md:-translate-y-1/2 md:opacity-0 md:group-hover/upload:pointer-events-auto md:group-hover/upload:opacity-100\"\n type=\"button\"\n @click=\"emit('removeFile')\">\n {{ translate('apiClient.requestTableRow.delete') }}\n </button>\n </template>\n <template v-else>\n <div class=\"p-0.5\">\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 h-fit border-0 py-px shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"emit('uploadFile')\">\n <span>{{ translate('apiClient.requestTableRow.selectFile') }}</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </div>\n </template>\n </DataTableCell>\n </DataTableRow>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RequestTableRow.vue.js","names":[],"sources":["../../../../../src/v2/blocks/request-block/components/RequestTableRow.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components/button'\nimport { ScalarIcon } from '@scalar/components/icon'\nimport { ScalarIconButton } from '@scalar/components/icon-button'\nimport { ScalarIconGlobe, ScalarIconTrash } from '@scalar/icons'\nimport type { ApiReferenceEvents } from '@scalar/workspace-store/events'\nimport { unpackProxyObject } from '@scalar/workspace-store/helpers/unpack-proxy'\nimport { resolve } from '@scalar/workspace-store/resolve'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type {\n ParameterObject,\n SchemaObject,\n} from '@scalar/workspace-store/schemas/v3.2/strict/openapi-document'\nimport { computed, ref, watch } from 'vue'\n\nimport { getFileName } from '@/v2/blocks/request-block/helpers/files'\nimport { validateParameter } from '@/v2/blocks/request-block/helpers/validate-parameter'\nimport { CodeInputLite } from '@/v2/components/code-input'\nimport {\n DataTableCell,\n DataTableCheckbox,\n DataTableInputSelect,\n DataTableRow,\n} from '@/v2/components/data-table'\nimport { useLocalization } from '@/v2/features/localization'\n\nimport RequestTableTooltip from './RequestTableTooltip.vue'\n\nconst {\n data,\n environment,\n hasCheckboxDisabled,\n deferKeyUpdates,\n invalidParams,\n showUploadButton,\n} = defineProps<{\n data: TableRow\n /** Keep key edits local until blur when the row identity depends on its name. */\n deferKeyUpdates?: boolean\n hasCheckboxDisabled?: boolean\n invalidParams?: Set<string>\n label?: string\n environment: XScalarEnvironment\n showUploadButton?: boolean\n}>()\n\nconst emit = defineEmits<{\n (e: 'upsertRow', payload: TableRowUpsertPayload): void\n (e: 'deleteRow'): void\n (e: 'uploadFile'): void\n (e: 'removeFile'): void\n (e: 'navigate', route: NonNullable<TableRow['globalRoute']>): void\n /** Select a value for a grouped global cookie preset. */\n (e: 'selectPreset', value: string): void\n}>()\n\nconst { translate } = useLocalization()\n\nexport type TableRow = {\n /** The parameter or field name/key */\n name: string\n /** The parameter value, can be a string, file, or null */\n value: string | File | null\n /** Optional description for the parameter */\n description?: string\n /** Optional route for global parameters (e.g., cookies shared across workspace) */\n globalRoute?: ApiReferenceEvents['ui:navigate']\n /** Whether the parameter is disabled/inactive */\n isDisabled?: boolean\n /** Whether an optional parameter is disabled because it has no explicit x-disabled value */\n isDisabledByDefault?: boolean\n /** OpenAPI schema object with type, validation rules, examples, etc. */\n schema?: SchemaObject\n /** Preserve array values even while their JSON text is temporarily invalid. */\n isArray?: boolean\n /** Whether the parameter is required */\n isRequired?: boolean\n /**\n * Whether the parameter is readonly and can not be modifies directly\n * User can still override the parameter which is going to show up with the linethrough style\n */\n isReadonly?: boolean\n /** Whether the parameter is overridden later on */\n isOverridden?: boolean\n /** Track the original parameter so we can update it */\n originalParameter?: ParameterObject\n /** Path to a value inside the original parameter example for expanded object parameters */\n sourceParameterValuePath?: string[]\n /**\n * Selectable values for a grouped global cookie preset. When set, the value cell renders a\n * dropdown to switch between predefined values (for example a `Culture` cookie with `PL`/`EN`)\n * instead of an editable input.\n */\n presetOptions?: string[]\n}\n\nexport type TableRowUpsertPayload = {\n name: string\n value: string | File\n isDisabled: boolean\n shouldRenameExpandedRow?: boolean\n}\n\n/**\n * Track local state for the row\n *\n * Now this is required because of the way we get default values from the schema.\n * If we have a default value in data.value, then we update isDisabled to true. We lose the default value since now\n * we have an example where value: ''. This is why we need to track the local state and update all of the params at the\n * same time.\n */\nconst name = ref<string>(data.name ?? '')\nconst value = ref<string | File>(unpackProxyObject(data.value) ?? '')\nconst isDisabled = ref<boolean>(data.isDisabled ?? false)\n\n// Keep the above state synced with the data prop\nwatch(\n () => data.name,\n (newName) => {\n // Do not overwrite a valid local name with an empty incoming prop — this\n // can happen when the placeholder row (appended by displayData) is briefly\n // mapped onto this component instance before Vue re-keys the list.\n if (!newName && name.value && !deferKeyUpdates) return\n name.value = newName ?? ''\n },\n)\nwatch(\n () => data.value,\n (newValue) => (value.value = unpackProxyObject(newValue) ?? ''),\n)\nwatch(\n () => data.isDisabled,\n (newIsDisabled) => (isDisabled.value = newIsDisabled ?? false),\n)\n\n/** Check if the value is a File instance */\nconst isFile = computed(() => value.value instanceof File)\n\n/** Display value handles File instances and shows filename instead */\nconst displayValue = computed(\n () =>\n (isFile.value\n ? getFileName(value.value as File)\n : (value.value as string)) ?? '',\n)\n\nconst defaultValue = computed(() => data.schema?.default as string)\n\n/** See if we can extract enum values from the schema */\nconst enumValue = computed<string[]>(() => {\n if (!data.schema) {\n return []\n }\n\n // Grab the enum from the schema\n if (data.schema.enum) {\n return data.schema.enum.map((item) => String(item))\n }\n\n // Grab the enum from the items schema\n if ('items' in data.schema) {\n const resolved = resolve.schema(data.schema.items)\n if (resolved?.enum) {\n return resolved.enum.map((item) => String(item))\n }\n }\n\n return []\n})\n\nconst typeValue = computed(() =>\n data.schema && 'type' in data.schema ? data.schema.type : undefined,\n)\n\nconst validationResult = computed(() =>\n validateParameter(data.schema, value.value),\n)\n\n/** Handle row updates while preserving existing properties */\nconst handleUpdateRow = (\n payload: Partial<{ name: string; value: string; isDisabled: boolean }>,\n options: { shouldRenameExpandedRow?: boolean; commitKey?: boolean } = {},\n): void => {\n // Update our local state\n if (payload.name !== undefined) {\n name.value = payload.name\n }\n if (payload.value !== undefined) {\n value.value = payload.value\n\n // Optional parameters start unchecked, but entering a value expresses intent to send it.\n // Explicit x-disabled values stay unchanged so a deliberate opt-out is preserved.\n if (data.isDisabledByDefault) {\n isDisabled.value = false\n }\n }\n\n // Is disabled should only be updated when explicitly provided in the payload\n if (payload.isDisabled !== undefined) {\n isDisabled.value = payload.isDisabled\n }\n\n if (\n payload.name !== undefined &&\n (deferKeyUpdates || data.sourceParameterValuePath) &&\n !options.shouldRenameExpandedRow &&\n !options.commitKey\n ) {\n return\n }\n\n // Emit all of the local state\n emit('upsertRow', {\n name: name.value,\n value: value.value,\n isDisabled: isDisabled.value,\n ...(options.shouldRenameExpandedRow\n ? { shouldRenameExpandedRow: true }\n : {}),\n })\n}\n\n/**\n * Commit a key edit when the input loses focus. Body and expanded-object rows defer their rename to blur (see\n * handleUpdateRow), so we only emit when the key actually changed. Focusing and blurring the field\n * without typing should not re-emit the row or silently reset its disabled state. The current\n * disabled state is passed through so a renamed row keeps it.\n */\nconst handleKeyBlur = (newName: string): void => {\n if (newName === data.name) {\n return\n }\n // Do not emit an update that would blank the parameter name — this can fire\n // when CodeInputLite blurs before it has rendered its initial value.\n if (!newName && data.name && (!deferKeyUpdates || name.value !== '')) {\n return\n }\n\n handleUpdateRow(\n { name: newName, isDisabled: isDisabled.value },\n {\n shouldRenameExpandedRow: Boolean(data.sourceParameterValuePath),\n commitKey: true,\n },\n )\n}\n\n/** Save a pending body key before the send shortcut reaches the request handler. */\nconst handleKeydown = (event: KeyboardEvent): void => {\n if (\n deferKeyUpdates &&\n event.key === 'Enter' &&\n (event.metaKey || event.ctrlKey)\n ) {\n handleKeyBlur(name.value)\n }\n}\n</script>\n\n<template>\n <DataTableRow\n :id=\"data.name\"\n :class=\"{\n alert: validationResult.ok === false,\n error: validationResult.ok === false && invalidParams?.has(data.name),\n }\">\n <DataTableCheckbox\n :ariaLabel=\"\n translate('apiClient.requestTableRow.include', {\n name: data.name || translate('apiClient.requestTableRow.row'),\n })\n \"\n class=\"!border-r\"\n :disabled=\"hasCheckboxDisabled ?? false\"\n :modelValue=\"!isDisabled\"\n @update:modelValue=\"(v) => handleUpdateRow({ isDisabled: !v })\" />\n\n <!-- Name -->\n <DataTableCell>\n <CodeInputLite\n :aria-label=\"\n translate('apiClient.requestTableRow.keyLabel', { name: label ?? '' })\n \"\n :disabled=\"data.isReadonly\"\n :environment=\"environment\"\n :modelValue=\"name\"\n :placeholder=\"translate('apiClient.requestTableRow.key')\"\n :required=\"Boolean(data.isRequired)\"\n @blur=\"(v) => handleKeyBlur(v)\"\n @keydown.capture=\"handleKeydown\"\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ name: v })\" />\n </DataTableCell>\n\n <!-- Value -->\n <DataTableCell>\n <!-- Preset switcher for grouped global cookies that share a name -->\n <DataTableInputSelect\n v-if=\"data.presetOptions?.length\"\n :canAddCustomValue=\"false\"\n :modelValue=\"displayValue\"\n :value=\"data.presetOptions\"\n @update:modelValue=\"(v) => emit('selectPreset', v)\" />\n <CodeInputLite\n v-else\n :aria-label=\"\n translate('apiClient.requestTableRow.valueLabel', {\n name: label ?? '',\n })\n \"\n class=\"pr-6 group-hover:pr-10 group-has-[.code-input-lite__editor:focus]:pr-10\"\n :default=\"defaultValue\"\n :disabled=\"data.isReadonly\"\n :enum=\"enumValue\"\n :environment=\"environment\"\n :examples=\"\n data.schema?.examples?.map((example) => String(example)) ?? []\n \"\n :linethrough=\"data.isOverridden\"\n :modelValue=\"displayValue\"\n :placeholder=\"translate('apiClient.requestTableRow.value')\"\n :type=\"typeValue\"\n withFakeData\n @navigate=\"(route) => emit('navigate', route)\"\n @update:modelValue=\"(v) => handleUpdateRow({ value: v })\">\n <template #icon>\n <ScalarButton\n v-if=\"\n Boolean(data.name || value) &&\n !data.isRequired &&\n data.isReadonly !== true\n \"\n :aria-label=\"\n translate('apiClient.requestTableRow.deleteRow', {\n name: data.name || translate('apiClient.requestTableRow.row'),\n })\n \"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 hidden h-fit rounded p-1 group-hover:flex group-has-[.code-input-lite__editor:focus]:flex\"\n size=\"sm\"\n variant=\"ghost\"\n @click=\"emit('deleteRow')\">\n <ScalarIconTrash class=\"size-3.5\" />\n </ScalarButton>\n\n <ScalarIconButton\n v-if=\"data.globalRoute !== undefined\"\n class=\"text-c-2 hover:text-c-1 hover:bg-b-2 z-context -mr-0.5 h-fit\"\n :icon=\"ScalarIconGlobe\"\n :label=\"translate('apiClient.requestTableRow.globalCookieHint')\"\n size=\"xs\"\n tooltip=\"top\"\n variant=\"ghost\"\n @click=\"emit('navigate', data.globalRoute)\" />\n\n <RequestTableTooltip\n v-if=\"data.isReadonly\"\n :description=\"translate('apiClient.requestTableRow.readOnlyHint')\"\n :value=\"null\" />\n <RequestTableTooltip\n v-else-if=\"data.schema\"\n :description=\"data.description\"\n :schema=\"data.schema\"\n :value />\n </template>\n </CodeInputLite>\n </DataTableCell>\n\n <!-- File upload -->\n <DataTableCell\n v-if=\"showUploadButton\"\n class=\"group/upload flex items-center justify-center whitespace-nowrap\">\n <template v-if=\"isFile\">\n <div\n class=\"text-c-2 flex w-full max-w-[100%] items-center justify-center overflow-hidden p-1\">\n <span\n class=\"truncate\"\n :title=\"displayValue\"\n >{{ displayValue }}</span\n >\n </div>\n <button\n class=\"bg-b-2 mt-1 block rounded p-0.5 text-center text-xs font-medium md:pointer-events-none md:absolute md:inset-x-1 md:top-1/2 md:mt-0 md:-translate-y-1/2 md:opacity-0 md:group-hover/upload:pointer-events-auto md:group-hover/upload:opacity-100\"\n type=\"button\"\n @click=\"emit('removeFile')\">\n {{ translate('apiClient.requestTableRow.delete') }}\n </button>\n </template>\n <template v-else>\n <div class=\"p-0.5\">\n <ScalarButton\n class=\"bg-b-2 hover:bg-b-3 text-c-2 h-fit border-0 py-px shadow-none\"\n size=\"sm\"\n variant=\"outlined\"\n @click=\"emit('uploadFile')\">\n <span>{{ translate('apiClient.requestTableRow.selectFile') }}</span>\n <ScalarIcon\n class=\"ml-1\"\n icon=\"Upload\"\n size=\"xs\"\n thickness=\"2.5\" />\n </ScalarButton>\n </div>\n </template>\n </DataTableCell>\n </DataTableRow>\n</template>\n"],"mappings":""}
@@ -55,7 +55,7 @@ var RequestTableRow_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
55
55
  const value = ref(unpackProxyObject(__props.data.value) ?? "");
56
56
  const isDisabled = ref(__props.data.isDisabled ?? false);
57
57
  watch(() => __props.data.name, (newName) => {
58
- if (!newName && name.value) return;
58
+ if (!newName && name.value && !__props.deferKeyUpdates) return;
59
59
  name.value = newName ?? "";
60
60
  });
61
61
  watch(() => __props.data.value, (newValue) => value.value = unpackProxyObject(newValue) ?? "");
@@ -101,7 +101,7 @@ var RequestTableRow_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/
101
101
  */
102
102
  const handleKeyBlur = (newName) => {
103
103
  if (newName === __props.data.name) return;
104
- if (!newName && __props.data.name) return;
104
+ if (!newName && __props.data.name && (!__props.deferKeyUpdates || name.value !== "")) return;
105
105
  handleUpdateRow({
106
106
  name: newName,
107
107
  isDisabled: isDisabled.value