@scalar/api-client 3.3.0 → 3.4.0

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 (191) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/monacoeditorwork/yaml.worker.bundle.js +92 -79
  3. package/dist/style.css +107 -73
  4. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts +2 -0
  5. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
  6. package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
  7. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +5 -0
  8. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
  9. package/dist/v2/blocks/operation-block/components/Header.vue.d.ts +4 -0
  10. package/dist/v2/blocks/operation-block/components/Header.vue.d.ts.map +1 -1
  11. package/dist/v2/blocks/operation-block/components/Header.vue.js +1 -1
  12. package/dist/v2/blocks/operation-block/components/Header.vue.js.map +1 -1
  13. package/dist/v2/blocks/operation-block/components/Header.vue.script.js +6 -0
  14. package/dist/v2/blocks/operation-block/components/Header.vue.script.js.map +1 -1
  15. package/dist/v2/blocks/operation-block/helpers/response-cache.d.ts +2 -1
  16. package/dist/v2/blocks/operation-block/helpers/response-cache.d.ts.map +1 -1
  17. package/dist/v2/blocks/operation-block/helpers/response-cache.js +9 -2
  18. package/dist/v2/blocks/operation-block/helpers/response-cache.js.map +1 -1
  19. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
  20. package/dist/v2/blocks/request-block/components/RequestTable.vue.d.ts +2 -2
  21. package/dist/v2/blocks/request-block/components/RequestTableRow.vue.d.ts +2 -2
  22. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts +4 -0
  23. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts.map +1 -1
  24. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js +1 -1
  25. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js.map +1 -1
  26. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js +84 -71
  27. package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js.map +1 -1
  28. package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.d.ts +13 -0
  29. package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.d.ts.map +1 -0
  30. package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.js +28 -0
  31. package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.js.map +1 -0
  32. package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.d.ts +16 -0
  33. package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.d.ts.map +1 -0
  34. package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.js +28 -0
  35. package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.js.map +1 -0
  36. package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.d.ts +31 -0
  37. package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.d.ts.map +1 -0
  38. package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.js +18 -0
  39. package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.js.map +1 -0
  40. package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
  41. package/dist/v2/components/code-input/CodeInput.vue.js +1 -1
  42. package/dist/v2/components/code-input/CodeInput.vue.js.map +1 -1
  43. package/dist/v2/components/code-input/CodeInput.vue.script.js +1 -1
  44. package/dist/v2/components/code-input/CodeInput.vue.script.js.map +1 -1
  45. package/dist/v2/components/modals/ModalClientContainer.vue.d.ts +2 -2
  46. package/dist/v2/components/modals/ModalClientContainer.vue.d.ts.map +1 -1
  47. package/dist/v2/components/modals/ModalClientContainer.vue.js +1 -1
  48. package/dist/v2/components/modals/ModalClientContainer.vue.js.map +1 -1
  49. package/dist/v2/components/modals/ModalClientContainer.vue.script.js +17 -24
  50. package/dist/v2/components/modals/ModalClientContainer.vue.script.js.map +1 -1
  51. package/dist/v2/constants.js +1 -1
  52. package/dist/v2/features/app/App.vue.d.ts +25 -1
  53. package/dist/v2/features/app/App.vue.d.ts.map +1 -1
  54. package/dist/v2/features/app/App.vue.js.map +1 -1
  55. package/dist/v2/features/app/App.vue.script.js +54 -39
  56. package/dist/v2/features/app/App.vue.script.js.map +1 -1
  57. package/dist/v2/features/app/app-events.js +4 -4
  58. package/dist/v2/features/app/app-events.js.map +1 -1
  59. package/dist/v2/features/app/app-state.d.ts +20 -14
  60. package/dist/v2/features/app/app-state.d.ts.map +1 -1
  61. package/dist/v2/features/app/app-state.js +89 -55
  62. package/dist/v2/features/app/app-state.js.map +1 -1
  63. package/dist/v2/features/app/components/AppHeader.vue.d.ts +26 -3
  64. package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
  65. package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
  66. package/dist/v2/features/app/components/AppHeader.vue.script.js +15 -6
  67. package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
  68. package/dist/v2/features/app/components/AppSidebar.vue.d.ts +2 -2
  69. package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
  70. package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
  71. package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
  72. package/dist/v2/features/app/components/AppSidebar.vue.script.js +86 -108
  73. package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
  74. package/dist/v2/features/app/components/CreateVersionModal.vue.d.ts +28 -0
  75. package/dist/v2/features/app/components/CreateVersionModal.vue.d.ts.map +1 -0
  76. package/dist/v2/features/app/components/CreateVersionModal.vue.js +7 -0
  77. package/dist/v2/features/app/components/CreateVersionModal.vue.js.map +1 -0
  78. package/dist/v2/features/app/components/CreateVersionModal.vue.script.js +84 -0
  79. package/dist/v2/features/app/components/CreateVersionModal.vue.script.js.map +1 -0
  80. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts +26 -0
  81. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts.map +1 -0
  82. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js +9 -0
  83. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js.map +1 -0
  84. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js +376 -0
  85. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js.map +1 -0
  86. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts +16 -0
  87. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts.map +1 -0
  88. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js +7 -0
  89. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js.map +1 -0
  90. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js +51 -0
  91. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js.map +1 -0
  92. package/dist/v2/features/app/components/SidebarDocument.vue.d.ts +45 -0
  93. package/dist/v2/features/app/components/SidebarDocument.vue.d.ts.map +1 -0
  94. package/dist/v2/features/app/components/SidebarDocument.vue.js +7 -0
  95. package/dist/v2/features/app/components/SidebarDocument.vue.js.map +1 -0
  96. package/dist/v2/features/app/components/SidebarDocument.vue.script.js +137 -0
  97. package/dist/v2/features/app/components/SidebarDocument.vue.script.js.map +1 -0
  98. package/dist/v2/features/app/helpers/check-version-conflict.d.ts +51 -0
  99. package/dist/v2/features/app/helpers/check-version-conflict.d.ts.map +1 -0
  100. package/dist/v2/features/app/helpers/check-version-conflict.js +79 -0
  101. package/dist/v2/features/app/helpers/check-version-conflict.js.map +1 -0
  102. package/dist/v2/features/app/helpers/compute-version-status.d.ts +45 -0
  103. package/dist/v2/features/app/helpers/compute-version-status.d.ts.map +1 -0
  104. package/dist/v2/features/app/helpers/compute-version-status.js +18 -0
  105. package/dist/v2/features/app/helpers/compute-version-status.js.map +1 -0
  106. package/dist/v2/features/app/helpers/create-draft-registry-document.d.ts +39 -0
  107. package/dist/v2/features/app/helpers/create-draft-registry-document.d.ts.map +1 -0
  108. package/dist/v2/features/app/helpers/create-draft-registry-document.js +64 -0
  109. package/dist/v2/features/app/helpers/create-draft-registry-document.js.map +1 -0
  110. package/dist/v2/features/app/helpers/create-temp-operation.d.ts.map +1 -1
  111. package/dist/v2/features/app/helpers/create-temp-operation.js +5 -8
  112. package/dist/v2/features/app/helpers/create-temp-operation.js.map +1 -1
  113. package/dist/v2/features/app/helpers/detect-document-conflicts.d.ts +26 -0
  114. package/dist/v2/features/app/helpers/detect-document-conflicts.d.ts.map +1 -0
  115. package/dist/v2/features/app/helpers/detect-document-conflicts.js +27 -0
  116. package/dist/v2/features/app/helpers/detect-document-conflicts.js.map +1 -0
  117. package/dist/v2/features/app/helpers/filter-workspaces.d.ts +14 -14
  118. package/dist/v2/features/app/helpers/filter-workspaces.d.ts.map +1 -1
  119. package/dist/v2/features/app/helpers/filter-workspaces.js +15 -15
  120. package/dist/v2/features/app/helpers/filter-workspaces.js.map +1 -1
  121. package/dist/v2/features/app/helpers/group-workspaces.d.ts +23 -3
  122. package/dist/v2/features/app/helpers/group-workspaces.d.ts.map +1 -1
  123. package/dist/v2/features/app/helpers/group-workspaces.js +22 -7
  124. package/dist/v2/features/app/helpers/group-workspaces.js.map +1 -1
  125. package/dist/v2/features/app/helpers/load-registry-document.d.ts +16 -1
  126. package/dist/v2/features/app/helpers/load-registry-document.d.ts.map +1 -1
  127. package/dist/v2/features/app/helpers/load-registry-document.js +7 -6
  128. package/dist/v2/features/app/helpers/load-registry-document.js.map +1 -1
  129. package/dist/v2/features/app/helpers/routes.d.ts +5 -1
  130. package/dist/v2/features/app/helpers/routes.d.ts.map +1 -1
  131. package/dist/v2/features/app/helpers/routes.js +1 -1
  132. package/dist/v2/features/app/helpers/routes.js.map +1 -1
  133. package/dist/v2/features/app/helpers/version-status-presentation.d.ts +24 -0
  134. package/dist/v2/features/app/helpers/version-status-presentation.d.ts.map +1 -0
  135. package/dist/v2/features/app/helpers/version-status-presentation.js +43 -0
  136. package/dist/v2/features/app/helpers/version-status-presentation.js.map +1 -0
  137. package/dist/v2/features/app/hooks/use-active-document-version.d.ts +41 -0
  138. package/dist/v2/features/app/hooks/use-active-document-version.d.ts.map +1 -0
  139. package/dist/v2/features/app/hooks/use-active-document-version.js +60 -0
  140. package/dist/v2/features/app/hooks/use-active-document-version.js.map +1 -0
  141. package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts +71 -23
  142. package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts.map +1 -1
  143. package/dist/v2/features/app/hooks/use-sidebar-documents.js +167 -45
  144. package/dist/v2/features/app/hooks/use-sidebar-documents.js.map +1 -1
  145. package/dist/v2/features/app/hooks/use-version-conflict-check.d.ts +35 -0
  146. package/dist/v2/features/app/hooks/use-version-conflict-check.d.ts.map +1 -0
  147. package/dist/v2/features/app/hooks/use-version-conflict-check.js +62 -0
  148. package/dist/v2/features/app/hooks/use-version-conflict-check.js.map +1 -0
  149. package/dist/v2/features/collection/DocumentCollection.vue.d.ts.map +1 -1
  150. package/dist/v2/features/collection/DocumentCollection.vue.js.map +1 -1
  151. package/dist/v2/features/collection/DocumentCollection.vue.script.js +6 -1
  152. package/dist/v2/features/collection/DocumentCollection.vue.script.js.map +1 -1
  153. package/dist/v2/features/collection/OperationCollection.vue.script.js +1 -0
  154. package/dist/v2/features/collection/OperationCollection.vue.script.js.map +1 -1
  155. package/dist/v2/features/collection/WorkspaceCollection.vue.script.js +1 -0
  156. package/dist/v2/features/collection/WorkspaceCollection.vue.script.js.map +1 -1
  157. package/dist/v2/features/collection/components/Authentication.vue.script.js +1 -0
  158. package/dist/v2/features/collection/components/Authentication.vue.script.js.map +1 -1
  159. package/dist/v2/features/collection/components/Cookies.vue.script.js +1 -0
  160. package/dist/v2/features/collection/components/Cookies.vue.script.js.map +1 -1
  161. package/dist/v2/features/collection/components/Editor/Editor.vue.script.js +1 -0
  162. package/dist/v2/features/collection/components/Editor/Editor.vue.script.js.map +1 -1
  163. package/dist/v2/features/collection/components/Environment.vue.script.js +1 -0
  164. package/dist/v2/features/collection/components/Environment.vue.script.js.map +1 -1
  165. package/dist/v2/features/collection/components/GetStarted.vue.d.ts +12 -4
  166. package/dist/v2/features/collection/components/GetStarted.vue.d.ts.map +1 -1
  167. package/dist/v2/features/collection/components/GetStarted.vue.js.map +1 -1
  168. package/dist/v2/features/collection/components/GetStarted.vue.script.js +56 -13
  169. package/dist/v2/features/collection/components/GetStarted.vue.script.js.map +1 -1
  170. package/dist/v2/features/collection/components/Overview.vue.script.js +1 -0
  171. package/dist/v2/features/collection/components/Overview.vue.script.js.map +1 -1
  172. package/dist/v2/features/collection/components/Runner/components/Runner.vue.script.js +1 -0
  173. package/dist/v2/features/collection/components/Runner/components/Runner.vue.script.js.map +1 -1
  174. package/dist/v2/features/collection/components/Runner/hooks/use-runner-execution.js +2 -2
  175. package/dist/v2/features/collection/components/Runner/hooks/use-runner-execution.js.map +1 -1
  176. package/dist/v2/features/collection/components/Scripts.vue.script.js +1 -0
  177. package/dist/v2/features/collection/components/Scripts.vue.script.js.map +1 -1
  178. package/dist/v2/features/collection/components/Servers.vue.script.js +1 -0
  179. package/dist/v2/features/collection/components/Servers.vue.script.js.map +1 -1
  180. package/dist/v2/features/collection/components/Settings.vue.script.js +1 -0
  181. package/dist/v2/features/collection/components/Settings.vue.script.js.map +1 -1
  182. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js +1 -1
  183. package/dist/v2/features/operation/Operation.vue.d.ts.map +1 -1
  184. package/dist/v2/features/operation/Operation.vue.js.map +1 -1
  185. package/dist/v2/features/operation/Operation.vue.script.js +3 -0
  186. package/dist/v2/features/operation/Operation.vue.script.js.map +1 -1
  187. package/dist/v2/helpers/safe-run.d.ts +25 -1
  188. package/dist/v2/helpers/safe-run.d.ts.map +1 -1
  189. package/dist/v2/helpers/safe-run.js +26 -2
  190. package/dist/v2/helpers/safe-run.js.map +1 -1
  191. package/package.json +17 -16
@@ -1 +1 @@
1
- {"version":3,"file":"AddressBar.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIcon,\n ScalarWrappingText,\n} from '@scalar/components'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { REQUEST_METHODS } from '@scalar/helpers/http/http-info'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport { useClipboard } from '@scalar/use-hooks/useClipboard'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n getEnvironmentVariables,\n getResolvedUrl,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport {\n computed,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n history,\n server,\n servers,\n environment,\n serverMeta,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n}>()\n\nconst id = useId()\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\n/** Calculate the style for the address bar */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${REQUEST_METHODS[method].colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<HttpMethodType | null>(null)\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/** Clear conflict state when switching to a different operation */\nwatch(\n () => [path, method],\n () => {\n pathConflict.value = null\n methodConflict.value = null\n },\n)\n\n/** Check if the path contains a server URL, extract it, and select or add the server */\nconst checkForServer = (targetPath: string) => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, newPath] = extracted\n\n // Server is already selected — nothing to change\n if (url === server?.url) {\n return newPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n\n // Select the server if it already exists in the list\n if (matchingServer) {\n eventBus.emit('server:update:selected', {\n url,\n meta: serverMeta,\n })\n }\n // Otherwise add it as a new operation-level server\n else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: {\n type: 'operation',\n path,\n method,\n },\n })\n }\n\n return newPath\n}\n\n/** Emit the path/method update event with conflict handling */\nconst emitPathMethodUpdate = (\n targetMethod: HttpMethodType,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n const newPath = checkForServer(targetPath)\n const normalizedPath = newPath.startsWith('/') ? newPath : `/${newPath}`\n\n // Update the local state of codemirror so we don't have werid path on conflict\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, blurTargetSelector) => {\n // Clear conflicts if the operation was successful or no change was made\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n }\n // Otherwise set the conflict if needed\n else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path unchanged.\n // The CodeMirror DOM still shows the full URL, so we force it back to just the path.\n if (\n status === 'no-change' &&\n addressBarRef.value?.codeMirrorRef?.textContent &&\n addressBarRef.value.codeMirrorRef.textContent !== newPath\n ) {\n addressBarRef.value.setCodeMirrorContent(newPath)\n }\n\n // Re-trigger the click or focus event if we have a blur target selector\n if (blurTargetSelector) {\n const element = document.querySelector(blurTargetSelector)\n\n // Re-trigger clicks on buttons\n if (element instanceof HTMLButtonElement) {\n element.click()\n }\n\n // Re-trigger focus on inputs and codeInputs\n else if (\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n (element instanceof HTMLElement &&\n element.getAttribute('contenteditable') === 'true')\n ) {\n element.focus()\n }\n }\n },\n })\n}\n\n/** Update the operation's HTTP method, handling conflicts */\nconst handleMethodChange = (newMethod: HttpMethodType): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Update the operation's path, handling conflicts also we extract the blur target selector to re-trigger click events\n *\n * We have special handling for the tab key to prevent it from triggering a click on the focused button\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget = event.relatedTarget as Element | null\n const blurTargetSelector = tabbedOut.value ? null : getSelector(relatedTarget)\n\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Lets unset the server when backspace is pressed and the path is empty */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if ((event.target as HTMLElement)?.innerText === '\\n') {\n eventBus.emit('server:update:selected', {\n url: '',\n meta: serverMeta,\n })\n }\n}\n\n/** Handle path submit (Enter key) — saves the path and triggers execution via blurTargetSelector */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Handle focus events */\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\nconst tabbedOut = ref(false)\nconst handleFocusSendButton = () => sendButtonRef.value?.$el?.focus()\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n) => {\n // If it already has focus we just propagate native behavior which should focus the browser address bar\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('/')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\nonMounted(() => {\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar)\n eventBus.on('ui:focus:send-button', handleFocusSendButton)\n eventBus.on('copy-url:address-bar', copyUrl)\n eventBus.on('hooks:on:request:sent', startLoading)\n eventBus.on('hooks:on:request:complete', stopLoading)\n})\n\nonBeforeUnmount(() => {\n eventBus.off('ui:focus:address-bar', handleFocusAddressBar)\n eventBus.off('ui:focus:send-button', handleFocusSendButton)\n eventBus.off('copy-url:address-bar', copyUrl)\n eventBus.off('hooks:on:request:sent', startLoading)\n eventBus.off('hooks:on:request:complete', stopLoading)\n\n // Stop the animation when the component is unmounted\n // This is to prevent the animation from continuing after the component is unmounted\n stopLoading()\n})\n\nconst { copyToClipboard } = useClipboard()\n\n/** Copy the resolved URL with the environment variables to the clipboard */\nconst copyUrl = async () => {\n const resolvedUrl = getResolvedUrl({ server, path })\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrlWithEnvVars = replaceEnvVariables(\n resolvedUrl,\n environmentVariables,\n )\n await copyToClipboard(resolvedUrlWithEnvVars)\n}\n\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n/** Whether either dropdown is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\nconst navigateToServersPage = () => {\n if (serverMeta.type === 'operation') {\n return eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n }\n return eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'servers',\n })\n}\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <div\n :id=\"id\"\n class=\"scalar-address-bar order-last flex h-(--scalar-address-bar-height) w-full [--scalar-address-bar-height:32px] lg:order-0 lg:w-auto\">\n <!-- Address Bar -->\n <div\n class=\"address-bar-bg-states text-xxs group relative order-last flex w-full max-w-[calc(100dvw-24px)] flex-1 flex-row items-stretch rounded-lg p-0.75 lg:order-none lg:max-w-[580px] lg:min-w-[580px] xl:max-w-[720px] xl:min-w-[720px]\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n <div class=\"flex gap-1\">\n <HttpMethod\n :isEditable=\"layout !== 'modal'\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"\n (payload) => eventBus.emit('server:update:selected', payload)\n \"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"\n (payload) => eventBus.emit('server:update:variables', payload)\n \" />\n\n <div class=\"fade-left\" />\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n aria-label=\"Path\"\n class=\"min-w-fit outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal'\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"server ? '' : 'Enter a URL'\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n <div class=\"fade-right\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"copyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">Copy URL</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n A\n <em>{{ methodConflict?.toUpperCase() ?? method.toUpperCase() }}</em>\n request to\n <ScalarWrappingText :text=\"pathConflict ?? path\" />\n already exists in this document\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs hidden lg:flex\">Send</span>\n </span>\n <span class=\"sr-only\">\n Send {{ method }} request to {{ server?.url ?? '' }}{{ path }}\n </span>\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n scroll-timeline: --scroll-timeline x;\n /* Firefox supports */\n scroll-timeline: --scroll-timeline horizontal;\n -ms-overflow-style: none; /* IE and Edge */\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n.scroll-timeline-x-address:empty:before {\n content: 'Enter URL or cURL request';\n color: var(--scalar-color-3);\n pointer-events: none;\n}\n.fade-left,\n.fade-right {\n content: '';\n position: sticky;\n height: 100%;\n animation-name: fadein;\n animation-duration: 1ms;\n animation-direction: reverse;\n animation-timeline: --scroll-timeline;\n pointer-events: none;\n z-index: 1;\n}\n.fade-left {\n background: linear-gradient(\n -90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n left: -1px;\n min-width: 6px;\n animation-direction: normal;\n}\n.fade-right {\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n right: -1px;\n min-width: 24px;\n}\n@keyframes fadein {\n 0% {\n opacity: 0;\n }\n 1% {\n opacity: 1;\n }\n}\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":""}
1
+ {"version":3,"file":"AddressBar.vue.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** Openapi document slug */\n documentSlug: string\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIcon,\n ScalarWrappingText,\n} from '@scalar/components'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { REQUEST_METHODS } from '@scalar/helpers/http/http-info'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport { useClipboard } from '@scalar/use-hooks/useClipboard'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n getEnvironmentVariables,\n getResolvedUrl,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { getOperationExampleKey } from '@/v2/blocks/operation-block/helpers/response-cache'\nimport { isPlaceholderPath } from '@/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path'\nimport { refocusBlurTarget } from '@/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { usePathMasking } from '@/v2/blocks/scalar-address-bar-block/hooks/use-path-masking'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n exampleKey,\n documentSlug,\n server,\n servers,\n environment,\n serverMeta,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n}>()\n\n// ───────────────────────────────────────────────────────────────────\n// Template refs & reactive state\n// ───────────────────────────────────────────────────────────────────\n\nconst id = useId()\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\n\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<HttpMethodType | null>(null)\nconst tabbedOut = ref(false)\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n// ───────────────────────────────────────────────────────────────────\n// Derived state\n// ───────────────────────────────────────────────────────────────────\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\n/** Animated background transform for the loading indicator */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${REQUEST_METHODS[method].colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/** Whether either dropdown (server or history) is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\n/** Uniquely identifies the currently selected operation + example */\nconst uniqueKey = computed(() =>\n getOperationExampleKey(method, path, exampleKey, documentSlug),\n)\n\n/** Clear conflict state when switching to a different operation */\nwatch(uniqueKey, () => {\n pathConflict.value = null\n methodConflict.value = null\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Focus helpers\n// ───────────────────────────────────────────────────────────────────\n\nconst handleFocusSendButton = (): void => sendButtonRef.value?.$el?.focus()\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n): void => {\n // On non-desktop layouts, if already focused we let the browser handle the\n // native behavior (e.g. selecting the browser's own address bar).\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path masking\n//\n// Drafts on `/` and auto-generated `/_scalar_temp...` paths are internal\n// placeholders — the address bar focuses and clears on mount and on\n// navigation so the user sees a blank prompt instead of the internal\n// path. The deferred mask only clears when CodeMirror still contains that\n// placeholder path, so a user edit that lands before the frame is preserved.\n// ───────────────────────────────────────────────────────────────────\n\nusePathMasking({\n isReady: () => addressBarRef.value?.codeMirror,\n operationKey: () => uniqueKey.value,\n shouldMask: () => isPlaceholderPath(path, documentSlug),\n // Defer to the next frame so focus() runs after click-handler side\n // effects that move focus (e.g. a dropdown refocusing its trigger),\n // which would otherwise blur our input and emit a spurious path\n // update against the now-empty value.\n onMask: () =>\n requestAnimationFrame(() => {\n const editorContent =\n addressBarRef.value?.codeMirror?.state.doc.toString()\n\n if (editorContent && editorContent !== path) {\n return\n }\n\n handleFocusAddressBar({ clear: true })\n }),\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Server extraction\n// ───────────────────────────────────────────────────────────────────\n\n/** If the path contains a server URL, extract it and select or add that server */\nconst extractAndSelectServer = (targetPath: string): string => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, remainingPath] = extracted\n\n // Server is already selected — nothing to do\n if (url === server?.url) {\n return remainingPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n if (matchingServer) {\n eventBus.emit('server:update:selected', { url, meta: serverMeta })\n } else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: { type: 'operation', path, method },\n })\n }\n\n return remainingPath\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path / method updates\n// ───────────────────────────────────────────────────────────────────\n\nconst normalizePath = (value: string): string =>\n value.startsWith('/') ? value : `/${value}`\n\n/** Emit a path/method update and reconcile conflicts + cursor state on the result */\nconst emitPathMethodUpdate = (\n targetMethod: HttpMethodType,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n const extractedPath = extractAndSelectServer(targetPath)\n const normalizedPath = normalizePath(extractedPath)\n\n // Keep CodeMirror in sync so a conflict does not leave a stale value on screen\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, returnedSelector) => {\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n } else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n\n return\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path\n // unchanged. CodeMirror still shows the full URL, so force it back to\n // just the path.\n const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent\n if (\n status === 'no-change' &&\n mirrorContent &&\n mirrorContent !== extractedPath\n ) {\n addressBarRef.value?.setCodeMirrorContent(extractedPath)\n }\n\n nextTick(() => refocusBlurTarget(returnedSelector))\n },\n })\n}\n\n/** Change the operation's method, preferring the conflicting path if present */\nconst handleMethodChange = (newMethod: HttpMethodType): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Save the path on blur and replay the click that caused the blur (so e.g. a\n * click on the Send button still fires after the async update resolves).\n * Tab-outs explicitly do not replay — tabbing to a button should not trigger\n * its click.\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget = event.relatedTarget as Element | null\n const blurTargetSelector = tabbedOut.value ? null : getSelector(relatedTarget)\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Save the path on Enter and trigger the Send button after the update resolves */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener from also handling this Enter press\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Unset the server when backspace is pressed on an empty path */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if ((event.target as HTMLElement)?.innerText === '\\n') {\n eventBus.emit('server:update:selected', { url: '', meta: serverMeta })\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Clipboard\n// ───────────────────────────────────────────────────────────────────\n\nconst { copyToClipboard } = useClipboard()\n\n/** Copy the fully resolved URL (with environment variables applied) */\nconst copyUrl = async (): Promise<void> => {\n const resolvedUrl = getResolvedUrl({ server, path })\n const variables = getEnvironmentVariables(environment)\n await copyToClipboard(replaceEnvVariables(resolvedUrl, variables))\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Navigation\n// ───────────────────────────────────────────────────────────────────\n\nconst navigateToServersPage = (): void => {\n if (serverMeta.type === 'operation') {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n return\n }\n\n eventBus.emit('ui:navigate', { page: 'document', path: 'servers' })\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Lifecycle\n// ───────────────────────────────────────────────────────────────────\n\nconst unsubscribes: Array<() => void> = []\n\nonMounted(() => {\n unsubscribes.push(\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar),\n eventBus.on('ui:focus:send-button', handleFocusSendButton),\n eventBus.on('copy-url:address-bar', copyUrl),\n eventBus.on('hooks:on:request:sent', startLoading),\n eventBus.on('hooks:on:request:complete', stopLoading),\n )\n})\n\nonBeforeUnmount(() => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n\n // Prevents the loading animation from continuing after unmount\n stopLoading()\n})\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <div\n :id=\"id\"\n class=\"scalar-address-bar order-last flex h-(--scalar-address-bar-height) w-full [--scalar-address-bar-height:32px] lg:order-0 lg:w-auto\">\n <!-- Address Bar -->\n <div\n class=\"address-bar-bg-states text-xxs group relative order-last flex w-full max-w-[calc(100dvw-24px)] flex-1 flex-row items-stretch rounded-lg p-0.75 lg:order-none lg:max-w-[580px] lg:min-w-[580px] xl:max-w-[720px] xl:min-w-[720px]\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n <div class=\"flex gap-1\">\n <HttpMethod\n :isEditable=\"layout !== 'modal'\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"\n (payload) => eventBus.emit('server:update:selected', payload)\n \"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"\n (payload) => eventBus.emit('server:update:variables', payload)\n \" />\n\n <div class=\"fade-left\" />\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n aria-label=\"Path\"\n class=\"min-w-fit pl-px outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal'\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"server ? '' : 'Enter a URL'\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n <div class=\"fade-right\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"copyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">Copy URL</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n A\n <em>{{ methodConflict?.toUpperCase() ?? method.toUpperCase() }}</em>\n request to\n <ScalarWrappingText :text=\"pathConflict ?? path\" />\n already exists in this document\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs hidden lg:flex\">Send</span>\n </span>\n <span class=\"sr-only\">\n Send {{ method }} request to {{ server?.url ?? '' }}{{ path }}\n </span>\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n scroll-timeline: --scroll-timeline x;\n /* Firefox supports */\n scroll-timeline: --scroll-timeline horizontal;\n -ms-overflow-style: none; /* IE and Edge */\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n.scroll-timeline-x-address:empty:before {\n content: 'Enter URL or cURL request';\n color: var(--scalar-color-3);\n pointer-events: none;\n}\n.fade-left,\n.fade-right {\n content: '';\n position: sticky;\n height: 100%;\n animation-name: fadein;\n animation-duration: 1ms;\n animation-direction: reverse;\n animation-timeline: --scroll-timeline;\n pointer-events: none;\n z-index: 1;\n}\n.fade-left {\n background: linear-gradient(\n -90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n left: -1px;\n min-width: 6px;\n animation-direction: normal;\n}\n.fade-right {\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n right: -1px;\n min-width: 24px;\n}\n@keyframes fadein {\n 0% {\n opacity: 0;\n }\n 1% {\n opacity: 1;\n }\n}\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":""}
@@ -1,9 +1,13 @@
1
+ import { getOperationExampleKey } from "../../operation-block/helpers/response-cache.js";
1
2
  import CodeInput_default from "../../../components/code-input/CodeInput.vue.js";
2
3
  import HttpMethod_default from "../../../../components/HttpMethod/HttpMethod.vue.js";
4
+ import { isPlaceholderPath } from "../helpers/is-placeholder-path.js";
5
+ import { refocusBlurTarget } from "../helpers/refocus-blur-target.js";
3
6
  import { useLoadingAnimation } from "../hooks/use-loading-animation.js";
7
+ import { usePathMasking } from "../hooks/use-path-masking.js";
4
8
  import ServerDropdown_default from "../../../components/server/ServerDropdown.vue.js";
5
9
  import AddressBarHistory_default from "./AddressBarHistory.vue.js";
6
- import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, toDisplayString, unref, useId, useTemplateRef, watch, withCtx, withKeys } from "vue";
10
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, toDisplayString, unref, useId, useTemplateRef, watch, withCtx, withKeys } from "vue";
7
11
  import { ScalarButton, ScalarIcon, ScalarWrappingText } from "@scalar/components";
8
12
  import { ScalarIconCopy, ScalarIconWarningCircle } from "@scalar/icons";
9
13
  import { getEnvironmentVariables, getResolvedUrl } from "@scalar/workspace-store/request-example";
@@ -33,6 +37,8 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
33
37
  props: {
34
38
  path: {},
35
39
  method: {},
40
+ documentSlug: {},
41
+ exampleKey: {},
36
42
  server: {},
37
43
  servers: {},
38
44
  history: {},
@@ -45,29 +51,55 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
45
51
  setup(__props, { expose: __expose, emit: __emit }) {
46
52
  const emit = __emit;
47
53
  const id = useId();
54
+ const sendButtonRef = useTemplateRef("sendButtonRef");
55
+ const addressBarRef = useTemplateRef("addressBarRef");
48
56
  const { percentage, startLoading, stopLoading, isLoading } = useLoadingAnimation();
49
- /** Calculate the style for the address bar */
57
+ const pathConflict = ref(null);
58
+ const methodConflict = ref(null);
59
+ const tabbedOut = ref(false);
60
+ const isServerDropdownOpen = ref(false);
61
+ const isHistoryDropdownOpen = ref(false);
62
+ /** Keeps the cursor visible past the fade-right overlay while typing */
63
+ const addressBarScrollMargins = EditorView.scrollMargins.of(() => ({ right: 24 }));
64
+ /** Animated background transform for the loading indicator */
50
65
  const style = computed(() => ({
51
66
  backgroundColor: `color-mix(in srgb, transparent 90%, ${REQUEST_METHODS[__props.method].colorVar})`,
52
67
  transform: `translate3d(-${percentage.value}%,0,0)`
53
68
  }));
54
- /** Keeps the cursor visible past the fade-right overlay while typing */
55
- const addressBarScrollMargins = EditorView.scrollMargins.of(() => ({ right: 24 }));
56
- const pathConflict = ref(null);
57
- const methodConflict = ref(null);
58
69
  /** Whether there is a path or method conflict */
59
70
  const hasConflict = computed(() => methodConflict.value || pathConflict.value);
71
+ /** Whether either dropdown (server or history) is open */
72
+ const isDropdownOpen = computed(() => isServerDropdownOpen.value || isHistoryDropdownOpen.value);
73
+ /** Uniquely identifies the currently selected operation + example */
74
+ const uniqueKey = computed(() => getOperationExampleKey(__props.method, __props.path, __props.exampleKey, __props.documentSlug));
60
75
  /** Clear conflict state when switching to a different operation */
61
- watch(() => [__props.path, __props.method], () => {
76
+ watch(uniqueKey, () => {
62
77
  pathConflict.value = null;
63
78
  methodConflict.value = null;
64
79
  });
65
- /** Check if the path contains a server URL, extract it, and select or add the server */
66
- const checkForServer = (targetPath) => {
80
+ const handleFocusSendButton = () => sendButtonRef.value?.$el?.focus();
81
+ const handleFocusAddressBar = (payload) => {
82
+ if (addressBarRef.value?.isFocused && __props.layout !== "desktop") return;
83
+ addressBarRef.value?.focus("end");
84
+ if (payload && "clear" in payload && payload.clear) addressBarRef.value?.setCodeMirrorContent("");
85
+ if (payload && "event" in payload) payload.event.preventDefault();
86
+ };
87
+ usePathMasking({
88
+ isReady: () => addressBarRef.value?.codeMirror,
89
+ operationKey: () => uniqueKey.value,
90
+ shouldMask: () => isPlaceholderPath(__props.path, __props.documentSlug),
91
+ onMask: () => requestAnimationFrame(() => {
92
+ const editorContent = addressBarRef.value?.codeMirror?.state.doc.toString();
93
+ if (editorContent && editorContent !== __props.path) return;
94
+ handleFocusAddressBar({ clear: true });
95
+ })
96
+ });
97
+ /** If the path contains a server URL, extract it and select or add that server */
98
+ const extractAndSelectServer = (targetPath) => {
67
99
  const extracted = extractServerFromPath(targetPath);
68
100
  if (!extracted) return targetPath;
69
- const [url, newPath] = extracted;
70
- if (url === __props.server?.url) return newPath;
101
+ const [url, remainingPath] = extracted;
102
+ if (url === __props.server?.url) return remainingPath;
71
103
  if (__props.servers.find((s) => s.url === url)) __props.eventBus.emit("server:update:selected", {
72
104
  url,
73
105
  meta: __props.serverMeta
@@ -81,12 +113,13 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
81
113
  method: __props.method
82
114
  }
83
115
  });
84
- return newPath;
116
+ return remainingPath;
85
117
  };
86
- /** Emit the path/method update event with conflict handling */
118
+ const normalizePath = (value) => value.startsWith("/") ? value : `/${value}`;
119
+ /** Emit a path/method update and reconcile conflicts + cursor state on the result */
87
120
  const emitPathMethodUpdate = (targetMethod, targetPath, blurTargetSelector = null) => {
88
- const newPath = checkForServer(targetPath);
89
- const normalizedPath = newPath.startsWith("/") ? newPath : `/${newPath}`;
121
+ const extractedPath = extractAndSelectServer(targetPath);
122
+ const normalizedPath = normalizePath(extractedPath);
90
123
  addressBarRef.value?.setCodeMirrorContent(normalizedPath);
91
124
  __props.eventBus.emit("operation:update:pathMethod", {
92
125
  meta: {
@@ -98,29 +131,28 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
98
131
  method: targetMethod,
99
132
  path: normalizedPath
100
133
  },
101
- callback: (status, blurTargetSelector) => {
134
+ callback: (status, returnedSelector) => {
102
135
  if (status === "success" || status === "no-change") {
103
136
  methodConflict.value = null;
104
137
  pathConflict.value = null;
105
138
  } else if (status === "conflict") {
106
139
  if (targetMethod !== __props.method) methodConflict.value = targetMethod;
107
140
  if (normalizedPath !== __props.path) pathConflict.value = normalizedPath;
141
+ return;
108
142
  }
109
- if (status === "no-change" && addressBarRef.value?.codeMirrorRef?.textContent && addressBarRef.value.codeMirrorRef.textContent !== newPath) addressBarRef.value.setCodeMirrorContent(newPath);
110
- if (blurTargetSelector) {
111
- const element = document.querySelector(blurTargetSelector);
112
- if (element instanceof HTMLButtonElement) element.click();
113
- else if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLElement && element.getAttribute("contenteditable") === "true") element.focus();
114
- }
143
+ const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent;
144
+ if (status === "no-change" && mirrorContent && mirrorContent !== extractedPath) addressBarRef.value?.setCodeMirrorContent(extractedPath);
145
+ nextTick(() => refocusBlurTarget(returnedSelector));
115
146
  }
116
147
  });
117
148
  };
118
- /** Update the operation's HTTP method, handling conflicts */
149
+ /** Change the operation's method, preferring the conflicting path if present */
119
150
  const handleMethodChange = (newMethod) => emitPathMethodUpdate(newMethod, pathConflict.value ?? __props.path);
120
151
  /**
121
- * Update the operation's path, handling conflicts also we extract the blur target selector to re-trigger click events
122
- *
123
- * We have special handling for the tab key to prevent it from triggering a click on the focused button
152
+ * Save the path on blur and replay the click that caused the blur (so e.g. a
153
+ * click on the Send button still fires after the async update resolves).
154
+ * Tab-outs explicitly do not replay tabbing to a button should not trigger
155
+ * its click.
124
156
  */
125
157
  const handlePathBlur = (newPath, event) => {
126
158
  const relatedTarget = event.relatedTarget;
@@ -128,68 +160,49 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
128
160
  tabbedOut.value = false;
129
161
  emitPathMethodUpdate(methodConflict.value ?? __props.method, newPath, blurTargetSelector);
130
162
  };
131
- /** Lets unset the server when backspace is pressed and the path is empty */
163
+ /** Save the path on Enter and trigger the Send button after the update resolves */
164
+ const handlePathSubmit = (newPath, event) => {
165
+ event.stopPropagation();
166
+ emitPathMethodUpdate(methodConflict.value ?? __props.method, newPath, "[data-addressbar-action=\"send\"]");
167
+ };
168
+ /** Unset the server when backspace is pressed on an empty path */
132
169
  const handlePathBackspace = (event) => {
133
170
  if (event.target?.innerText === "\n") __props.eventBus.emit("server:update:selected", {
134
171
  url: "",
135
172
  meta: __props.serverMeta
136
173
  });
137
174
  };
138
- /** Handle path submit (Enter key) — saves the path and triggers execution via blurTargetSelector */
139
- const handlePathSubmit = (newPath, event) => {
140
- event.stopPropagation();
141
- emitPathMethodUpdate(methodConflict.value ?? __props.method, newPath, "[data-addressbar-action=\"send\"]");
142
- };
143
- /** Handle focus events */
144
- const sendButtonRef = useTemplateRef("sendButtonRef");
145
- const addressBarRef = useTemplateRef("addressBarRef");
146
- const tabbedOut = ref(false);
147
- const handleFocusSendButton = () => sendButtonRef.value?.$el?.focus();
148
- const handleFocusAddressBar = (payload) => {
149
- if (addressBarRef.value?.isFocused && __props.layout !== "desktop") return;
150
- addressBarRef.value?.focus("end");
151
- if (payload && "clear" in payload && payload.clear) addressBarRef.value?.setCodeMirrorContent("/");
152
- if (payload && "event" in payload) payload.event.preventDefault();
153
- };
154
- onMounted(() => {
155
- __props.eventBus.on("ui:focus:address-bar", handleFocusAddressBar);
156
- __props.eventBus.on("ui:focus:send-button", handleFocusSendButton);
157
- __props.eventBus.on("copy-url:address-bar", copyUrl);
158
- __props.eventBus.on("hooks:on:request:sent", startLoading);
159
- __props.eventBus.on("hooks:on:request:complete", stopLoading);
160
- });
161
- onBeforeUnmount(() => {
162
- __props.eventBus.off("ui:focus:address-bar", handleFocusAddressBar);
163
- __props.eventBus.off("ui:focus:send-button", handleFocusSendButton);
164
- __props.eventBus.off("copy-url:address-bar", copyUrl);
165
- __props.eventBus.off("hooks:on:request:sent", startLoading);
166
- __props.eventBus.off("hooks:on:request:complete", stopLoading);
167
- stopLoading();
168
- });
169
175
  const { copyToClipboard } = useClipboard();
170
- /** Copy the resolved URL with the environment variables to the clipboard */
176
+ /** Copy the fully resolved URL (with environment variables applied) */
171
177
  const copyUrl = async () => {
172
178
  await copyToClipboard(replaceEnvVariables(getResolvedUrl({
173
179
  server: __props.server,
174
180
  path: __props.path
175
181
  }), getEnvironmentVariables(__props.environment)));
176
182
  };
177
- const isServerDropdownOpen = ref(false);
178
- const isHistoryDropdownOpen = ref(false);
179
- /** Whether either dropdown is open */
180
- const isDropdownOpen = computed(() => isServerDropdownOpen.value || isHistoryDropdownOpen.value);
181
183
  const navigateToServersPage = () => {
182
- if (__props.serverMeta.type === "operation") return __props.eventBus.emit("ui:navigate", {
183
- page: "operation",
184
- path: "servers",
185
- operationPath: __props.serverMeta.path,
186
- method: __props.serverMeta.method
187
- });
188
- return __props.eventBus.emit("ui:navigate", {
184
+ if (__props.serverMeta.type === "operation") {
185
+ __props.eventBus.emit("ui:navigate", {
186
+ page: "operation",
187
+ path: "servers",
188
+ operationPath: __props.serverMeta.path,
189
+ method: __props.serverMeta.method
190
+ });
191
+ return;
192
+ }
193
+ __props.eventBus.emit("ui:navigate", {
189
194
  page: "document",
190
195
  path: "servers"
191
196
  });
192
197
  };
198
+ const unsubscribes = [];
199
+ onMounted(() => {
200
+ unsubscribes.push(__props.eventBus.on("ui:focus:address-bar", handleFocusAddressBar), __props.eventBus.on("ui:focus:send-button", handleFocusSendButton), __props.eventBus.on("copy-url:address-bar", copyUrl), __props.eventBus.on("hooks:on:request:sent", startLoading), __props.eventBus.on("hooks:on:request:complete", stopLoading));
201
+ });
202
+ onBeforeUnmount(() => {
203
+ for (const unsubscribe of unsubscribes) unsubscribe();
204
+ stopLoading();
205
+ });
193
206
  __expose({
194
207
  methodConflict,
195
208
  pathConflict
@@ -238,7 +251,7 @@ var AddressBar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
238
251
  ref: addressBarRef,
239
252
  alwaysEmitChange: "",
240
253
  "aria-label": "Path",
241
- class: "min-w-fit outline-none",
254
+ class: "min-w-fit pl-px outline-none",
242
255
  disableCloseBrackets: "",
243
256
  disabled: __props.layout === "modal",
244
257
  disableEnter: "",
@@ -1 +1 @@
1
- {"version":3,"file":"AddressBar.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIcon,\n ScalarWrappingText,\n} from '@scalar/components'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { REQUEST_METHODS } from '@scalar/helpers/http/http-info'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport { useClipboard } from '@scalar/use-hooks/useClipboard'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n getEnvironmentVariables,\n getResolvedUrl,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport {\n computed,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n history,\n server,\n servers,\n environment,\n serverMeta,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n}>()\n\nconst id = useId()\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\n/** Calculate the style for the address bar */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${REQUEST_METHODS[method].colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<HttpMethodType | null>(null)\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/** Clear conflict state when switching to a different operation */\nwatch(\n () => [path, method],\n () => {\n pathConflict.value = null\n methodConflict.value = null\n },\n)\n\n/** Check if the path contains a server URL, extract it, and select or add the server */\nconst checkForServer = (targetPath: string) => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, newPath] = extracted\n\n // Server is already selected — nothing to change\n if (url === server?.url) {\n return newPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n\n // Select the server if it already exists in the list\n if (matchingServer) {\n eventBus.emit('server:update:selected', {\n url,\n meta: serverMeta,\n })\n }\n // Otherwise add it as a new operation-level server\n else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: {\n type: 'operation',\n path,\n method,\n },\n })\n }\n\n return newPath\n}\n\n/** Emit the path/method update event with conflict handling */\nconst emitPathMethodUpdate = (\n targetMethod: HttpMethodType,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n const newPath = checkForServer(targetPath)\n const normalizedPath = newPath.startsWith('/') ? newPath : `/${newPath}`\n\n // Update the local state of codemirror so we don't have werid path on conflict\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, blurTargetSelector) => {\n // Clear conflicts if the operation was successful or no change was made\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n }\n // Otherwise set the conflict if needed\n else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path unchanged.\n // The CodeMirror DOM still shows the full URL, so we force it back to just the path.\n if (\n status === 'no-change' &&\n addressBarRef.value?.codeMirrorRef?.textContent &&\n addressBarRef.value.codeMirrorRef.textContent !== newPath\n ) {\n addressBarRef.value.setCodeMirrorContent(newPath)\n }\n\n // Re-trigger the click or focus event if we have a blur target selector\n if (blurTargetSelector) {\n const element = document.querySelector(blurTargetSelector)\n\n // Re-trigger clicks on buttons\n if (element instanceof HTMLButtonElement) {\n element.click()\n }\n\n // Re-trigger focus on inputs and codeInputs\n else if (\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n (element instanceof HTMLElement &&\n element.getAttribute('contenteditable') === 'true')\n ) {\n element.focus()\n }\n }\n },\n })\n}\n\n/** Update the operation's HTTP method, handling conflicts */\nconst handleMethodChange = (newMethod: HttpMethodType): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Update the operation's path, handling conflicts also we extract the blur target selector to re-trigger click events\n *\n * We have special handling for the tab key to prevent it from triggering a click on the focused button\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget = event.relatedTarget as Element | null\n const blurTargetSelector = tabbedOut.value ? null : getSelector(relatedTarget)\n\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Lets unset the server when backspace is pressed and the path is empty */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if ((event.target as HTMLElement)?.innerText === '\\n') {\n eventBus.emit('server:update:selected', {\n url: '',\n meta: serverMeta,\n })\n }\n}\n\n/** Handle path submit (Enter key) — saves the path and triggers execution via blurTargetSelector */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Handle focus events */\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\nconst tabbedOut = ref(false)\nconst handleFocusSendButton = () => sendButtonRef.value?.$el?.focus()\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n) => {\n // If it already has focus we just propagate native behavior which should focus the browser address bar\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('/')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\nonMounted(() => {\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar)\n eventBus.on('ui:focus:send-button', handleFocusSendButton)\n eventBus.on('copy-url:address-bar', copyUrl)\n eventBus.on('hooks:on:request:sent', startLoading)\n eventBus.on('hooks:on:request:complete', stopLoading)\n})\n\nonBeforeUnmount(() => {\n eventBus.off('ui:focus:address-bar', handleFocusAddressBar)\n eventBus.off('ui:focus:send-button', handleFocusSendButton)\n eventBus.off('copy-url:address-bar', copyUrl)\n eventBus.off('hooks:on:request:sent', startLoading)\n eventBus.off('hooks:on:request:complete', stopLoading)\n\n // Stop the animation when the component is unmounted\n // This is to prevent the animation from continuing after the component is unmounted\n stopLoading()\n})\n\nconst { copyToClipboard } = useClipboard()\n\n/** Copy the resolved URL with the environment variables to the clipboard */\nconst copyUrl = async () => {\n const resolvedUrl = getResolvedUrl({ server, path })\n const environmentVariables = getEnvironmentVariables(environment)\n const resolvedUrlWithEnvVars = replaceEnvVariables(\n resolvedUrl,\n environmentVariables,\n )\n await copyToClipboard(resolvedUrlWithEnvVars)\n}\n\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n/** Whether either dropdown is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\nconst navigateToServersPage = () => {\n if (serverMeta.type === 'operation') {\n return eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n }\n return eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'servers',\n })\n}\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <div\n :id=\"id\"\n class=\"scalar-address-bar order-last flex h-(--scalar-address-bar-height) w-full [--scalar-address-bar-height:32px] lg:order-0 lg:w-auto\">\n <!-- Address Bar -->\n <div\n class=\"address-bar-bg-states text-xxs group relative order-last flex w-full max-w-[calc(100dvw-24px)] flex-1 flex-row items-stretch rounded-lg p-0.75 lg:order-none lg:max-w-[580px] lg:min-w-[580px] xl:max-w-[720px] xl:min-w-[720px]\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n <div class=\"flex gap-1\">\n <HttpMethod\n :isEditable=\"layout !== 'modal'\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"\n (payload) => eventBus.emit('server:update:selected', payload)\n \"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"\n (payload) => eventBus.emit('server:update:variables', payload)\n \" />\n\n <div class=\"fade-left\" />\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n aria-label=\"Path\"\n class=\"min-w-fit outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal'\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"server ? '' : 'Enter a URL'\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n <div class=\"fade-right\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"copyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">Copy URL</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n A\n <em>{{ methodConflict?.toUpperCase() ?? method.toUpperCase() }}</em>\n request to\n <ScalarWrappingText :text=\"pathConflict ?? path\" />\n already exists in this document\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs hidden lg:flex\">Send</span>\n </span>\n <span class=\"sr-only\">\n Send {{ method }} request to {{ server?.url ?? '' }}{{ path }}\n </span>\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n scroll-timeline: --scroll-timeline x;\n /* Firefox supports */\n scroll-timeline: --scroll-timeline horizontal;\n -ms-overflow-style: none; /* IE and Edge */\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n.scroll-timeline-x-address:empty:before {\n content: 'Enter URL or cURL request';\n color: var(--scalar-color-3);\n pointer-events: none;\n}\n.fade-left,\n.fade-right {\n content: '';\n position: sticky;\n height: 100%;\n animation-name: fadein;\n animation-duration: 1ms;\n animation-direction: reverse;\n animation-timeline: --scroll-timeline;\n pointer-events: none;\n z-index: 1;\n}\n.fade-left {\n background: linear-gradient(\n -90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n left: -1px;\n min-width: 6px;\n animation-direction: normal;\n}\n.fade-right {\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n right: -1px;\n min-width: 24px;\n}\n@keyframes fadein {\n 0% {\n opacity: 0;\n }\n 1% {\n opacity: 1;\n }\n}\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"AddressBar.vue.script.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/components/AddressBar.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * AddressBar component\n * This component is used to display the address bar for the operation block\n * It is used to display the path, method, server, and history for the operation\n */\nexport default {\n name: 'AddressBar',\n}\nexport type AddressBarProps = {\n /** Current request path */\n path: string\n /** Current request method */\n method: HttpMethodType\n /** Openapi document slug */\n documentSlug: string\n /** Currently selected example key for the current operation */\n exampleKey: string\n /** Currently selected server */\n server: ServerObject | null\n /** Server list available for operation/document */\n servers: ServerObject[]\n /** List of request history */\n history: History[]\n /** Client layout */\n layout: ClientLayout\n /** Event bus */\n eventBus: WorkspaceEventBus\n /** Environment */\n environment: XScalarEnvironment\n /** Meta information for the server */\n serverMeta: ServerMeta\n}\n</script>\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarIcon,\n ScalarWrappingText,\n} from '@scalar/components'\nimport { getSelector } from '@scalar/helpers/dom/get-selector'\nimport { REQUEST_METHODS } from '@scalar/helpers/http/http-info'\nimport type { HttpMethod as HttpMethodType } from '@scalar/helpers/http/http-methods'\nimport { replaceEnvVariables } from '@scalar/helpers/regex/replace-variables'\nimport { extractServerFromPath } from '@scalar/helpers/url/extract-server-from-path'\nimport { ScalarIconCopy, ScalarIconWarningCircle } from '@scalar/icons'\nimport { EditorView } from '@scalar/use-codemirror'\nimport { useClipboard } from '@scalar/use-hooks/useClipboard'\nimport type {\n ApiReferenceEvents,\n ServerMeta,\n WorkspaceEventBus,\n} from '@scalar/workspace-store/events'\nimport {\n getEnvironmentVariables,\n getResolvedUrl,\n} from '@scalar/workspace-store/request-example'\nimport type { XScalarEnvironment } from '@scalar/workspace-store/schemas/extensions/document/x-scalar-environments'\nimport type { ServerObject } from '@scalar/workspace-store/schemas/v3.1/strict/openapi-document'\nimport {\n computed,\n nextTick,\n onBeforeUnmount,\n onMounted,\n ref,\n useId,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { HttpMethod } from '@/components/HttpMethod'\nimport { getOperationExampleKey } from '@/v2/blocks/operation-block/helpers/response-cache'\nimport { isPlaceholderPath } from '@/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path'\nimport { refocusBlurTarget } from '@/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target'\nimport { useLoadingAnimation } from '@/v2/blocks/scalar-address-bar-block/hooks/use-loading-animation'\nimport { usePathMasking } from '@/v2/blocks/scalar-address-bar-block/hooks/use-path-masking'\nimport { CodeInput } from '@/v2/components/code-input'\nimport { ServerDropdown } from '@/v2/components/server'\nimport type { ClientLayout } from '@/v2/types/layout'\n\nimport AddressBarHistory, { type History } from './AddressBarHistory.vue'\n\nconst {\n path,\n method,\n layout,\n eventBus,\n exampleKey,\n documentSlug,\n server,\n servers,\n environment,\n serverMeta,\n} = defineProps<AddressBarProps>()\n\nconst emit = defineEmits<{\n /** Execute the current operation example */\n (e: 'execute'): void\n /** Select a request history item by index */\n (e: 'select:history:item', payload: { index: number }): void\n}>()\n\n// ───────────────────────────────────────────────────────────────────\n// Template refs & reactive state\n// ───────────────────────────────────────────────────────────────────\n\nconst id = useId()\nconst sendButtonRef = useTemplateRef('sendButtonRef')\nconst addressBarRef = useTemplateRef('addressBarRef')\n\nconst { percentage, startLoading, stopLoading, isLoading } =\n useLoadingAnimation()\n\nconst pathConflict = ref<string | null>(null)\nconst methodConflict = ref<HttpMethodType | null>(null)\nconst tabbedOut = ref(false)\nconst isServerDropdownOpen = ref(false)\nconst isHistoryDropdownOpen = ref(false)\n\n// ───────────────────────────────────────────────────────────────────\n// Derived state\n// ───────────────────────────────────────────────────────────────────\n\n/** Keeps the cursor visible past the fade-right overlay while typing */\nconst addressBarScrollMargins = EditorView.scrollMargins.of(() => ({\n right: 24,\n}))\n\n/** Animated background transform for the loading indicator */\nconst style = computed(() => ({\n backgroundColor: `color-mix(in srgb, transparent 90%, ${REQUEST_METHODS[method].colorVar})`,\n transform: `translate3d(-${percentage.value}%,0,0)`,\n}))\n\n/** Whether there is a path or method conflict */\nconst hasConflict = computed(() => methodConflict.value || pathConflict.value)\n\n/** Whether either dropdown (server or history) is open */\nconst isDropdownOpen = computed(\n () => isServerDropdownOpen.value || isHistoryDropdownOpen.value,\n)\n\n/** Uniquely identifies the currently selected operation + example */\nconst uniqueKey = computed(() =>\n getOperationExampleKey(method, path, exampleKey, documentSlug),\n)\n\n/** Clear conflict state when switching to a different operation */\nwatch(uniqueKey, () => {\n pathConflict.value = null\n methodConflict.value = null\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Focus helpers\n// ───────────────────────────────────────────────────────────────────\n\nconst handleFocusSendButton = (): void => sendButtonRef.value?.$el?.focus()\n\nconst handleFocusAddressBar = (\n payload: ApiReferenceEvents['ui:focus:address-bar'],\n): void => {\n // On non-desktop layouts, if already focused we let the browser handle the\n // native behavior (e.g. selecting the browser's own address bar).\n if (addressBarRef.value?.isFocused && layout !== 'desktop') {\n return\n }\n\n addressBarRef.value?.focus('end')\n\n if (payload && 'clear' in payload && payload.clear) {\n addressBarRef.value?.setCodeMirrorContent('')\n }\n\n if (payload && 'event' in payload) {\n payload.event.preventDefault()\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path masking\n//\n// Drafts on `/` and auto-generated `/_scalar_temp...` paths are internal\n// placeholders — the address bar focuses and clears on mount and on\n// navigation so the user sees a blank prompt instead of the internal\n// path. The deferred mask only clears when CodeMirror still contains that\n// placeholder path, so a user edit that lands before the frame is preserved.\n// ───────────────────────────────────────────────────────────────────\n\nusePathMasking({\n isReady: () => addressBarRef.value?.codeMirror,\n operationKey: () => uniqueKey.value,\n shouldMask: () => isPlaceholderPath(path, documentSlug),\n // Defer to the next frame so focus() runs after click-handler side\n // effects that move focus (e.g. a dropdown refocusing its trigger),\n // which would otherwise blur our input and emit a spurious path\n // update against the now-empty value.\n onMask: () =>\n requestAnimationFrame(() => {\n const editorContent =\n addressBarRef.value?.codeMirror?.state.doc.toString()\n\n if (editorContent && editorContent !== path) {\n return\n }\n\n handleFocusAddressBar({ clear: true })\n }),\n})\n\n// ───────────────────────────────────────────────────────────────────\n// Server extraction\n// ───────────────────────────────────────────────────────────────────\n\n/** If the path contains a server URL, extract it and select or add that server */\nconst extractAndSelectServer = (targetPath: string): string => {\n const extracted = extractServerFromPath(targetPath)\n if (!extracted) {\n return targetPath\n }\n\n const [url, remainingPath] = extracted\n\n // Server is already selected — nothing to do\n if (url === server?.url) {\n return remainingPath\n }\n\n const matchingServer = servers.find((s) => s.url === url)\n if (matchingServer) {\n eventBus.emit('server:update:selected', { url, meta: serverMeta })\n } else {\n eventBus.emit('server:add:server', {\n url,\n select: true,\n meta: { type: 'operation', path, method },\n })\n }\n\n return remainingPath\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Path / method updates\n// ───────────────────────────────────────────────────────────────────\n\nconst normalizePath = (value: string): string =>\n value.startsWith('/') ? value : `/${value}`\n\n/** Emit a path/method update and reconcile conflicts + cursor state on the result */\nconst emitPathMethodUpdate = (\n targetMethod: HttpMethodType,\n targetPath: string,\n blurTargetSelector: string | null = null,\n): void => {\n const extractedPath = extractAndSelectServer(targetPath)\n const normalizedPath = normalizePath(extractedPath)\n\n // Keep CodeMirror in sync so a conflict does not leave a stale value on screen\n addressBarRef.value?.setCodeMirrorContent(normalizedPath)\n\n eventBus.emit('operation:update:pathMethod', {\n meta: { method, path },\n blurTargetSelector,\n payload: { method: targetMethod, path: normalizedPath },\n callback: (status, returnedSelector) => {\n if (status === 'success' || status === 'no-change') {\n methodConflict.value = null\n pathConflict.value = null\n } else if (status === 'conflict') {\n if (targetMethod !== method) {\n methodConflict.value = targetMethod\n }\n if (normalizedPath !== path) {\n pathConflict.value = normalizedPath\n }\n\n return\n }\n\n // Edge case: pasting a full URL extracts the server but leaves the path\n // unchanged. CodeMirror still shows the full URL, so force it back to\n // just the path.\n const mirrorContent = addressBarRef.value?.codeMirrorRef?.textContent\n if (\n status === 'no-change' &&\n mirrorContent &&\n mirrorContent !== extractedPath\n ) {\n addressBarRef.value?.setCodeMirrorContent(extractedPath)\n }\n\n nextTick(() => refocusBlurTarget(returnedSelector))\n },\n })\n}\n\n/** Change the operation's method, preferring the conflicting path if present */\nconst handleMethodChange = (newMethod: HttpMethodType): void =>\n emitPathMethodUpdate(newMethod, pathConflict.value ?? path)\n\n/**\n * Save the path on blur and replay the click that caused the blur (so e.g. a\n * click on the Send button still fires after the async update resolves).\n * Tab-outs explicitly do not replay — tabbing to a button should not trigger\n * its click.\n */\nconst handlePathBlur = (newPath: string, event: FocusEvent): void => {\n const relatedTarget = event.relatedTarget as Element | null\n const blurTargetSelector = tabbedOut.value ? null : getSelector(relatedTarget)\n tabbedOut.value = false\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n blurTargetSelector,\n )\n}\n\n/** Save the path on Enter and trigger the Send button after the update resolves */\nconst handlePathSubmit = (\n newPath: string,\n event: KeyboardEvent | FocusEvent,\n): void => {\n // Prevent the global hotkey listener from also handling this Enter press\n event.stopPropagation()\n\n emitPathMethodUpdate(\n methodConflict.value ?? method,\n newPath,\n '[data-addressbar-action=\"send\"]',\n )\n}\n\n/** Unset the server when backspace is pressed on an empty path */\nconst handlePathBackspace = (event: KeyboardEvent): void => {\n if ((event.target as HTMLElement)?.innerText === '\\n') {\n eventBus.emit('server:update:selected', { url: '', meta: serverMeta })\n }\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Clipboard\n// ───────────────────────────────────────────────────────────────────\n\nconst { copyToClipboard } = useClipboard()\n\n/** Copy the fully resolved URL (with environment variables applied) */\nconst copyUrl = async (): Promise<void> => {\n const resolvedUrl = getResolvedUrl({ server, path })\n const variables = getEnvironmentVariables(environment)\n await copyToClipboard(replaceEnvVariables(resolvedUrl, variables))\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Navigation\n// ───────────────────────────────────────────────────────────────────\n\nconst navigateToServersPage = (): void => {\n if (serverMeta.type === 'operation') {\n eventBus.emit('ui:navigate', {\n page: 'operation',\n path: 'servers',\n operationPath: serverMeta.path,\n method: serverMeta.method,\n })\n return\n }\n\n eventBus.emit('ui:navigate', { page: 'document', path: 'servers' })\n}\n\n// ───────────────────────────────────────────────────────────────────\n// Lifecycle\n// ───────────────────────────────────────────────────────────────────\n\nconst unsubscribes: Array<() => void> = []\n\nonMounted(() => {\n unsubscribes.push(\n eventBus.on('ui:focus:address-bar', handleFocusAddressBar),\n eventBus.on('ui:focus:send-button', handleFocusSendButton),\n eventBus.on('copy-url:address-bar', copyUrl),\n eventBus.on('hooks:on:request:sent', startLoading),\n eventBus.on('hooks:on:request:complete', stopLoading),\n )\n})\n\nonBeforeUnmount(() => {\n for (const unsubscribe of unsubscribes) {\n unsubscribe()\n }\n\n // Prevents the loading animation from continuing after unmount\n stopLoading()\n})\n\ndefineExpose({\n methodConflict,\n pathConflict,\n})\n</script>\n<template>\n <div\n :id=\"id\"\n class=\"scalar-address-bar order-last flex h-(--scalar-address-bar-height) w-full [--scalar-address-bar-height:32px] lg:order-0 lg:w-auto\">\n <!-- Address Bar -->\n <div\n class=\"address-bar-bg-states text-xxs group relative order-last flex w-full max-w-[calc(100dvw-24px)] flex-1 flex-row items-stretch rounded-lg p-0.75 lg:order-none lg:max-w-[580px] lg:min-w-[580px] xl:max-w-[720px] xl:min-w-[720px]\"\n :class=\"{\n 'outline-c-danger outline': hasConflict,\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"pointer-events-none absolute top-0 left-0 block h-full w-full overflow-hidden rounded-lg border\"\n :class=\"{\n 'rounded-b-none': isDropdownOpen,\n }\">\n <div\n class=\"absolute top-0 left-0 h-full w-full\"\n :style />\n </div>\n <div class=\"flex gap-1\">\n <HttpMethod\n :isEditable=\"layout !== 'modal'\"\n isSquare\n :method=\"methodConflict ?? method\"\n teleport\n @change=\"handleMethodChange\" />\n </div>\n\n <div\n class=\"scroll-timeline-x scroll-timeline-x-hidden relative flex w-full bg-blend-normal\">\n <!-- Servers -->\n <ServerDropdown\n v-if=\"servers.length\"\n :layout=\"layout\"\n :meta=\"serverMeta\"\n :server=\"server\"\n :servers=\"servers\"\n :target=\"id\"\n @update:open=\"(value) => (isServerDropdownOpen = value)\"\n @update:selectedServer=\"\n (payload) => eventBus.emit('server:update:selected', payload)\n \"\n @update:servers=\"navigateToServersPage\"\n @update:variable=\"\n (payload) => eventBus.emit('server:update:variables', payload)\n \" />\n\n <div class=\"fade-left\" />\n <!-- Path + URL + env vars -->\n <CodeInput\n ref=\"addressBarRef\"\n alwaysEmitChange\n aria-label=\"Path\"\n class=\"min-w-fit pl-px outline-none\"\n disableCloseBrackets\n :disabled=\"layout === 'modal'\"\n disableEnter\n disableTabIndent\n :emitOnBlur=\"false\"\n :environment=\"environment\"\n :extensions=\"[addressBarScrollMargins]\"\n importCurl\n :layout=\"layout\"\n :modelValue=\"path\"\n :placeholder=\"server ? '' : 'Enter a URL'\"\n server\n @blur=\"handlePathBlur\"\n @keydown.delete=\"handlePathBackspace\"\n @keydown.tab=\"tabbedOut = true\"\n @submit=\"handlePathSubmit\" />\n <div class=\"fade-right\" />\n </div>\n\n <!-- Copy url button -->\n <ScalarButton\n class=\"hover:bg-b-3 mx-1\"\n size=\"xs\"\n variant=\"ghost\"\n @click=\"copyUrl\">\n <ScalarIconCopy />\n <span class=\"sr-only\">Copy URL</span>\n </ScalarButton>\n\n <AddressBarHistory\n :history=\"history\"\n :target=\"id\"\n @select:history:item=\"(payload) => emit('select:history:item', payload)\"\n @update:open=\"(value) => (isHistoryDropdownOpen = value)\" />\n <!-- Error message -->\n <div\n v-if=\"hasConflict\"\n class=\"z-context absolute inset-x-0 top-[calc(100%+4px)] flex flex-col items-center rounded px-6\">\n <div\n class=\"text-c-danger bg-b-danger border-c-danger flex items-center gap-1 rounded border p-1\">\n <ScalarIconWarningCircle size=\"sm\" />\n <div class=\"min-w-0 flex-1\">\n A\n <em>{{ methodConflict?.toUpperCase() ?? method.toUpperCase() }}</em>\n request to\n <ScalarWrappingText :text=\"pathConflict ?? path\" />\n already exists in this document\n </div>\n </div>\n </div>\n\n <ScalarButton\n ref=\"sendButtonRef\"\n class=\"relative h-auto shrink-0 overflow-hidden py-1 pr-2.5 pl-2 font-bold\"\n data-addressbar-action=\"send\"\n :disabled=\"isLoading\"\n @click=\"emit('execute')\">\n <span\n aria-hidden=\"true\"\n class=\"inline-flex items-center gap-1\">\n <ScalarIcon\n class=\"relative shrink-0 fill-current\"\n icon=\"Play\"\n size=\"xs\" />\n <span class=\"text-xxs hidden lg:flex\">Send</span>\n </span>\n <span class=\"sr-only\">\n Send {{ method }} request to {{ server?.url ?? '' }}{{ path }}\n </span>\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n:deep(.cm-editor) {\n height: 100%;\n outline: none;\n width: 100%;\n}\n:deep(.cm-line) {\n padding: 0;\n}\n:deep(.cm-content) {\n padding: 0;\n display: flex;\n align-items: center;\n font-size: var(--scalar-small);\n}\n.scroll-timeline-x {\n scroll-timeline: --scroll-timeline x;\n /* Firefox supports */\n scroll-timeline: --scroll-timeline horizontal;\n -ms-overflow-style: none; /* IE and Edge */\n}\n.scroll-timeline-x-hidden {\n overflow-x: auto;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller) {\n scrollbar-width: none;\n -ms-overflow-style: none;\n padding-right: 20px;\n overflow: auto;\n}\n.scroll-timeline-x-hidden::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-hidden :deep(.cm-scroller::-webkit-scrollbar) {\n width: 0;\n height: 0;\n display: none;\n}\n.scroll-timeline-x-address {\n line-height: 27px;\n scrollbar-width: none; /* Firefox */\n}\n/* make clickable are to left of send button */\n.scroll-timeline-x-address:after {\n content: '';\n position: absolute;\n height: 100%;\n width: 24px;\n right: 0;\n cursor: text;\n}\n.scroll-timeline-x-address:empty:before {\n content: 'Enter URL or cURL request';\n color: var(--scalar-color-3);\n pointer-events: none;\n}\n.fade-left,\n.fade-right {\n content: '';\n position: sticky;\n height: 100%;\n animation-name: fadein;\n animation-duration: 1ms;\n animation-direction: reverse;\n animation-timeline: --scroll-timeline;\n pointer-events: none;\n z-index: 1;\n}\n.fade-left {\n background: linear-gradient(\n -90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n left: -1px;\n min-width: 6px;\n animation-direction: normal;\n}\n.fade-right {\n background: linear-gradient(\n 90deg,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 100%) 0%,\n color-mix(in srgb, var(--scalar-address-bar-bg), transparent 20%) 30%,\n var(--scalar-address-bar-bg) 100%\n );\n right: -1px;\n min-width: 24px;\n}\n@keyframes fadein {\n 0% {\n opacity: 0;\n }\n 1% {\n opacity: 1;\n }\n}\n.address-bar-bg-states {\n --scalar-address-bar-bg: color-mix(\n in srgb,\n var(--scalar-background-1),\n var(--scalar-background-2)\n );\n background: var(--scalar-address-bar-bg);\n}\n.address-bar-bg-states:has(.cm-focused) {\n --scalar-address-bar-bg: var(--scalar-background-1);\n border-color: var(--scalar-border-color);\n outline-width: 1px;\n outline-style: solid;\n}\n.address-bar-bg-states:has(.cm-focused) .fade-left,\n.address-bar-bg-states:has(.cm-focused) .fade-right {\n --scalar-address-bar-bg: var(--scalar-background-1);\n}\n</style>\n"],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Returns true when the given path is a placeholder that should be hidden
3
+ * from the user. Two cases qualify:
4
+ *
5
+ * 1. Drafts documents viewing the root path `/` (a new, empty draft).
6
+ * 2. Auto-generated temp paths created when the user adds a new operation.
7
+ *
8
+ * In both cases the path is an implementation detail rather than something
9
+ * the user authored, so the UI masks it by focusing the address bar and
10
+ * clearing the visible text.
11
+ */
12
+ export declare const isPlaceholderPath: (path: string, documentSlug: string) => boolean;
13
+ //# sourceMappingURL=is-placeholder-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-placeholder-path.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.ts"],"names":[],"mappings":"AASA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,EAAE,cAAc,MAAM,KAAG,OAKtE,CAAA"}
@@ -0,0 +1,28 @@
1
+ //#region src/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.ts
2
+ /**
3
+ * Matches auto-generated temp paths produced by `createTempOperation`.
4
+ *
5
+ * Temp paths look like `/_scalar_temp<hex>` (e.g. `/_scalar_temp1a2b3c4d`) —
6
+ * the suffix is a truncated UUID, which is always hex. The namespaced prefix
7
+ * avoids colliding with any real user-authored path.
8
+ */
9
+ var TEMP_PATH_PATTERN = /^\/_scalar_temp[a-f0-9]*$/i;
10
+ /**
11
+ * Returns true when the given path is a placeholder that should be hidden
12
+ * from the user. Two cases qualify:
13
+ *
14
+ * 1. Drafts documents viewing the root path `/` (a new, empty draft).
15
+ * 2. Auto-generated temp paths created when the user adds a new operation.
16
+ *
17
+ * In both cases the path is an implementation detail rather than something
18
+ * the user authored, so the UI masks it by focusing the address bar and
19
+ * clearing the visible text.
20
+ */
21
+ var isPlaceholderPath = (path, documentSlug) => {
22
+ if (documentSlug.toLowerCase() === "drafts" && path === "/") return true;
23
+ return TEMP_PATH_PATTERN.test(path);
24
+ };
25
+ //#endregion
26
+ export { isPlaceholderPath };
27
+
28
+ //# sourceMappingURL=is-placeholder-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-placeholder-path.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.ts"],"sourcesContent":["/**\n * Matches auto-generated temp paths produced by `createTempOperation`.\n *\n * Temp paths look like `/_scalar_temp<hex>` (e.g. `/_scalar_temp1a2b3c4d`) —\n * the suffix is a truncated UUID, which is always hex. The namespaced prefix\n * avoids colliding with any real user-authored path.\n */\nconst TEMP_PATH_PATTERN = /^\\/_scalar_temp[a-f0-9]*$/i\n\n/**\n * Returns true when the given path is a placeholder that should be hidden\n * from the user. Two cases qualify:\n *\n * 1. Drafts documents viewing the root path `/` (a new, empty draft).\n * 2. Auto-generated temp paths created when the user adds a new operation.\n *\n * In both cases the path is an implementation detail rather than something\n * the user authored, so the UI masks it by focusing the address bar and\n * clearing the visible text.\n */\nexport const isPlaceholderPath = (path: string, documentSlug: string): boolean => {\n if (documentSlug.toLowerCase() === 'drafts' && path === '/') {\n return true\n }\n return TEMP_PATH_PATTERN.test(path)\n}\n"],"mappings":";;;;;;;;AAOA,IAAM,oBAAoB;;;;;;;;;;;;AAa1B,IAAa,qBAAqB,MAAc,iBAAkC;AAChF,KAAI,aAAa,aAAa,KAAK,YAAY,SAAS,IACtD,QAAO;AAET,QAAO,kBAAkB,KAAK,KAAK"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Re-triggers the interaction that caused the address bar to blur.
3
+ *
4
+ * When the user clicks a button or focuses another input while the address
5
+ * bar is focused, CodeMirror's blur fires first and the path/method update
6
+ * event runs asynchronously. By the time the update resolves, the click has
7
+ * already been consumed — so we capture the blur target via a selector and
8
+ * replay the original interaction once the update completes.
9
+ *
10
+ * - Buttons get a synthetic click to complete the action the user started.
11
+ * - Text inputs and contenteditable elements regain focus so typing resumes
12
+ * where the user intended.
13
+ * - Anything else is ignored silently.
14
+ */
15
+ export declare const refocusBlurTarget: (selector: string | null) => void;
16
+ //# sourceMappingURL=refocus-blur-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refocus-blur-target.d.ts","sourceRoot":"","sources":["../../../../../src/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,GAAG,IAAI,KAAG,IAoB3D,CAAA"}
@@ -0,0 +1,28 @@
1
+ //#region src/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.ts
2
+ /**
3
+ * Re-triggers the interaction that caused the address bar to blur.
4
+ *
5
+ * When the user clicks a button or focuses another input while the address
6
+ * bar is focused, CodeMirror's blur fires first and the path/method update
7
+ * event runs asynchronously. By the time the update resolves, the click has
8
+ * already been consumed — so we capture the blur target via a selector and
9
+ * replay the original interaction once the update completes.
10
+ *
11
+ * - Buttons get a synthetic click to complete the action the user started.
12
+ * - Text inputs and contenteditable elements regain focus so typing resumes
13
+ * where the user intended.
14
+ * - Anything else is ignored silently.
15
+ */
16
+ var refocusBlurTarget = (selector) => {
17
+ if (!selector) return;
18
+ const element = document.querySelector(selector);
19
+ if (element instanceof HTMLButtonElement) {
20
+ element.click();
21
+ return;
22
+ }
23
+ if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLElement && element.getAttribute("contenteditable") === "true") element.focus();
24
+ };
25
+ //#endregion
26
+ export { refocusBlurTarget };
27
+
28
+ //# sourceMappingURL=refocus-blur-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refocus-blur-target.js","names":[],"sources":["../../../../../src/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.ts"],"sourcesContent":["/**\n * Re-triggers the interaction that caused the address bar to blur.\n *\n * When the user clicks a button or focuses another input while the address\n * bar is focused, CodeMirror's blur fires first and the path/method update\n * event runs asynchronously. By the time the update resolves, the click has\n * already been consumed — so we capture the blur target via a selector and\n * replay the original interaction once the update completes.\n *\n * - Buttons get a synthetic click to complete the action the user started.\n * - Text inputs and contenteditable elements regain focus so typing resumes\n * where the user intended.\n * - Anything else is ignored silently.\n */\nexport const refocusBlurTarget = (selector: string | null): void => {\n if (!selector) {\n return\n }\n\n const element = document.querySelector(selector)\n\n if (element instanceof HTMLButtonElement) {\n element.click()\n return\n }\n\n const isEditable =\n element instanceof HTMLInputElement ||\n element instanceof HTMLTextAreaElement ||\n (element instanceof HTMLElement && element.getAttribute('contenteditable') === 'true')\n\n if (isEditable) {\n ;(element as HTMLElement).focus()\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAcA,IAAa,qBAAqB,aAAkC;AAClE,KAAI,CAAC,SACH;CAGF,MAAM,UAAU,SAAS,cAAc,SAAS;AAEhD,KAAI,mBAAmB,mBAAmB;AACxC,UAAQ,OAAO;AACf;;AAQF,KAJE,mBAAmB,oBACnB,mBAAmB,uBAClB,mBAAmB,eAAe,QAAQ,aAAa,kBAAkB,KAAK,OAG7E,SAAwB,OAAO"}