diff --git a/.gitignore b/.gitignore index b80772c83c..46bc383130 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,6 @@ uv.lock profiling_results.html -mkdocs/docs/reference/api/http/openapi.json +mkdocs/docs/reference/http/openapi.json mkdocs/docs/reference/api/rest/openapi.json mkdocs/docs/reference/plugins/rest/rest_plugin_openapi.json diff --git a/contributing/DOCS.md b/contributing/DOCS.md index 08c899f34f..663e5c4c77 100644 --- a/contributing/DOCS.md +++ b/contributing/DOCS.md @@ -111,6 +111,32 @@ The build creates `.well-known/skills/` directory structure for skills discovery - Generates `.well-known/skills/index.json` - Copies SKILL.md to both `.well-known/skills/dstack/` and site root +#### 4. HTTP API reference + +The HTTP API reference is generated from the FastAPI OpenAPI schema: + +- `scripts/docs/gen_openapi_reference.py` writes `mkdocs/docs/reference/http/openapi.json`, + keeps the per-tag Markdown pages in sync, and updates the generated tag list in the HTTP API + index page. +- Tag pages use `!!swagger openapi.json tag=""!!`. Keep tag names exactly as they appear + in the OpenAPI schema. +- `scripts/docs/hooks.py` expands the `!!swagger` directive into the Swagger UI container and + the hidden operation headings that MkDocs uses for the page table of contents. +- `mkdocs/assets/javascripts/swagger.js` loads the shared `openapi.json`, filters it by tag on + the client, and adapts Swagger UI markup to the docs layout. +- `mkdocs/assets/stylesheets/swagger.css` contains Swagger-specific styling and should stay + scoped under `.dstack-swagger-ui`. + +Keep hook logic limited to build-time Markdown/page structure, generated assets, and data +attributes needed by the client. Small presentation changes belong in `swagger.css`; small +behavior changes belong in `swagger.js`. + +If the HTTP API reference needs deeper structural customization, such as replacing major Swagger +UI panels, request/response rendering, model rendering, or "try it out" behavior, prefer moving +toward a dedicated local bundle or custom Swagger UI layout instead of adding more DOM patching. +That bundle can still use the single generated `openapi.json` and filter by tag on the client, so +we should not reintroduce per-tag OpenAPI files unless there is a concrete reason. + ### File structure ``` diff --git a/mkdocs.yml b/mkdocs.yml index 5d723bd0c9..0bab8c3294 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -136,7 +136,34 @@ plugins: "examples/clusters/a4/index.md": "docs/examples/clusters/gcp.md" "examples/clusters/efa/index.md": "docs/examples/clusters/aws.md" "docs/guides/migration.md": "docs/guides/upgrade.md" - "docs/reference/api/rest/index.md": "docs/reference/api/http/index.md" + "docs/reference/api/rest/index.md": "docs/reference/http/index.md" + "docs/reference/api/http/index.md": "docs/reference/http/index.md" + "docs/reference/api/http/auth.md": "docs/reference/http/authentication.md" + "docs/reference/api/http/backends.md": "docs/reference/http/backends.md" + "docs/reference/api/http/default.md": "docs/reference/http/default.md" + "docs/reference/api/http/events.md": "docs/reference/http/events.md" + "docs/reference/api/http/exports.md": "docs/reference/http/exports.md" + "docs/reference/api/http/files.md": "docs/reference/http/files.md" + "docs/reference/api/http/fleets.md": "docs/reference/http/fleets.md" + "docs/reference/api/http/gateways.md": "docs/reference/http/gateways.md" + "docs/reference/api/http/gpus.md": "docs/reference/http/gpus.md" + "docs/reference/api/http/imports.md": "docs/reference/http/exports.md" + "docs/reference/api/http/instances.md": "docs/reference/http/fleets.md" + "docs/reference/api/http/logs.md": "docs/reference/http/logs.md" + "docs/reference/api/http/metrics.md": "docs/reference/http/metrics.md" + "docs/reference/api/http/model-proxy.md": "docs/reference/http/proxy.md" + "docs/reference/api/http/projects.md": "docs/reference/http/projects.md" + "docs/reference/api/http/prometheus.md": "docs/reference/http/metrics.md" + "docs/reference/api/http/repos.md": "docs/reference/http/repos.md" + "docs/reference/api/http/runs.md": "docs/reference/http/runs.md" + "docs/reference/api/http/secrets.md": "docs/reference/http/secrets.md" + "docs/reference/api/http/server.md": "docs/reference/http/server.md" + "docs/reference/api/http/service-proxy.md": "docs/reference/http/proxy.md" + "docs/reference/api/http/sshproxy.md": "docs/reference/http/proxy.md" + "docs/reference/api/http/templates.md": "docs/reference/http/templates.md" + "docs/reference/api/http/user-public-keys.md": "docs/reference/http/users.md" + "docs/reference/api/http/users.md": "docs/reference/http/users.md" + "docs/reference/api/http/volumes.md": "docs/reference/http/volumes.md" # Examples moved from /examples/ to /docs/examples/ "examples.md": "docs/examples.md" "examples/single-node-training/trl.md": "docs/examples/training/trl.md" @@ -178,8 +205,6 @@ plugins: options: docstring_style: google show_source: false - - render_swagger: - allow_arbitrary_locations: true # Extensions markdown_extensions: @@ -219,11 +244,15 @@ markdown_extensions: # Customization extra_css: + - https://unpkg.com/swagger-ui-dist@5.32.0/swagger-ui.css - assets/stylesheets/extra.css + - assets/stylesheets/swagger.css - assets/stylesheets/termynal.css - assets/stylesheets/landing.css - assets/stylesheets/pricing.css extra_javascript: + - https://unpkg.com/swagger-ui-dist@5.32.0/swagger-ui-bundle.js + - assets/javascripts/swagger.js - assets/javascripts/extra.js - assets/javascripts/pricing.js extra: @@ -270,6 +299,7 @@ nav: - Events: docs/concepts/events.md - Exports: docs/concepts/exports.md - Guides: + - CLI & API: docs/guides/cli-api.md - Server deployment: docs/guides/server-deployment.md - Troubleshooting: docs/guides/troubleshooting.md - More: @@ -330,16 +360,34 @@ nav: - dstack secret: docs/reference/cli/dstack/secret.md - dstack export: docs/reference/cli/dstack/export.md - dstack import: docs/reference/cli/dstack/import.md - - API: - - Python API: docs/reference/api/python/index.md - - HTTP API: docs/reference/api/http/index.md - - Environment variables: docs/reference/environment-variables.md - - .dstack/profiles.yml: docs/reference/profiles.yml.md - - Plugins: - - Python API: docs/reference/plugins/python/index.md - - REST API: docs/reference/plugins/rest/index.md - - llms-full.txt: https://dstack.ai/llms-full.txt - - skill.md: https://dstack.ai/skill.md + - HTTP API: + - users: docs/reference/http/users.md + - projects: docs/reference/http/projects.md + - backends: docs/reference/http/backends.md + - fleets: docs/reference/http/fleets.md + - runs: docs/reference/http/runs.md + - gateways: docs/reference/http/gateways.md + - volumes: docs/reference/http/volumes.md + - logs: docs/reference/http/logs.md + - events: docs/reference/http/events.md + - repos: docs/reference/http/repos.md + - files: docs/reference/http/files.md + - secrets: docs/reference/http/secrets.md + - exports: docs/reference/http/exports.md + - metrics: docs/reference/http/metrics.md + - gpus: docs/reference/http/gpus.md + - templates: docs/reference/http/templates.md + - proxy: docs/reference/http/proxy.md + - authentication: docs/reference/http/authentication.md + - server: docs/reference/http/server.md + - Environment variables: docs/reference/env.md + - More: + - .dstack/profiles.yml: docs/reference/profiles.yml.md + - Plugins: + - Python API: docs/reference/plugins/python/index.md + - REST API: docs/reference/plugins/rest/index.md + - llms-full.txt: https://dstack.ai/llms-full.txt + - skill.md: https://dstack.ai/skill.md - Case studies: blog/case-studies.md - Benchmarks: blog/benchmarks.md - Blog: diff --git a/mkdocs/assets/javascripts/extra.js b/mkdocs/assets/javascripts/extra.js index 30bdcd5fd6..e21b22e6e0 100644 --- a/mkdocs/assets/javascripts/extra.js +++ b/mkdocs/assets/javascripts/extra.js @@ -15,13 +15,7 @@ controller = (function () { controller.init(); -function setupTermynal() { - document.querySelectorAll(".use-termynal").forEach(node => { - node.style.display = "block"; - new Termynal(node, { - lineDelay: 500 - }); - }); +function setupTermynal(root = document) { const progressLiteralStart = "---> 100%"; const promptLiteralStart = "$ "; const customPromptLiteralStart = "# "; @@ -29,12 +23,30 @@ function setupTermynal() { let termynals = []; function createTermynals() { - document + root .querySelectorAll(`.${termynalActivateClass} .highlight`) .forEach(node => { + const termynalRoot = node.closest(`.${termynalActivateClass}`); const text = node.textContent; + const singleInput = getTermynalOption(node, termynalRoot, "termynalSingleInput") === "true"; + const copyEnabled = getTermynalOption(node, termynalRoot, "termynalCopy") === "true"; + const instant = getTermynalOption(node, termynalRoot, "termynalInstant") === "true"; + const copyText = node.dstackTermynalCopyText || + termynalRoot?.dstackTermynalCopyText || + getTermynalOption(node, termynalRoot, "termynalCopyText") || + text.trimEnd(); + const maxHeight = getTermynalOption(node, termynalRoot, "termynalMaxHeight"); const lines = text.split(/(? .dstack-termy-copy"); + if (!button) { + button = document.createElement("button"); + button.className = "dstack-termy-copy"; + button.type = "button"; + button.title = "Copy"; + button.setAttribute("aria-label", "Copy"); + button.addEventListener("click", event => { + event.preventDefault(); + copyTermynalText(button.dstackTermynalCopyText || "").then(() => { + showTermynalCopiedHint(button); + }); + }); + container.appendChild(button); + } + button.dstackTermynalCopyText = copyText; + } + + function copyTermynalText(text) { + if (navigator.clipboard?.writeText) { + return navigator.clipboard.writeText(text).catch(() => copyTermynalTextFallback(text)); + } + return copyTermynalTextFallback(text); + } + + function copyTermynalTextFallback(text) { + const input = document.createElement("textarea"); + input.value = text; + input.setAttribute("readonly", ""); + input.style.position = "fixed"; + input.style.opacity = "0"; + document.body.appendChild(input); + input.select(); + document.execCommand("copy"); + input.remove(); + return Promise.resolve(); + } + + function showTermynalCopiedHint(button) { + button.classList.add("dstack-termy-copy-copied"); + window.clearTimeout(Number(button.dataset.termynalCopiedTimeout || 0)); + const timeout = window.setTimeout(() => { + button.classList.remove("dstack-termy-copy-copied"); + delete button.dataset.termynalCopiedTimeout; + }, 1300); + button.dataset.termynalCopiedTimeout = String(timeout); + } + + function escapeTermynalValue(value) { + return value + .replace(/&/g, "&") + .replace(//g, ">"); + } + function loadVisibleTermynals() { termynals = termynals.filter(termynal => { if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) { @@ -118,6 +211,9 @@ function setupTermynal() { } return true; }); + if (root !== document && termynals.length === 0) { + window.removeEventListener("scroll", loadVisibleTermynals); + } } window.addEventListener("scroll", loadVisibleTermynals); createTermynals(); @@ -135,6 +231,140 @@ function setupCustomCodeTitles() { }); } +function setupSensitiveTocActiveState() { + const toc = document.querySelector(".md-sidebar--secondary [data-md-component='toc']"); + if (!toc) { + return; + } + + let scheduled = false; + let syncing = false; + + function scheduleSync() { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(syncActiveTocLink); + } + + function syncActiveTocLink() { + scheduled = false; + + const items = getTocItems(toc); + if (items.length === 0) { + return; + } + + const activationTop = getTocActivationTop(); + let activeIndex = -1; + items.forEach((item, index) => { + if (item.target.getBoundingClientRect().top <= activationTop) { + activeIndex = index; + } + }); + + if (activeIndex === -1 && items[0].target.getBoundingClientRect().top <= window.innerHeight) { + activeIndex = 0; + } + + syncing = true; + items.forEach((item, index) => { + item.link.classList.toggle("md-nav__link--active", index === activeIndex); + item.link.classList.toggle("md-nav__link--passed", activeIndex !== -1 && index < activeIndex); + }); + window.requestAnimationFrame(() => { + syncing = false; + }); + } + + const observer = new MutationObserver(() => { + if (!syncing) { + scheduleSync(); + } + }); + observer.observe(toc, { + attributes: true, + attributeFilter: ["class"], + subtree: true, + }); + + toc.addEventListener("click", event => { + const target = event.target instanceof Element ? event.target : event.target.parentElement; + const link = target?.closest("a.md-nav__link[href*='#']"); + const heading = getTocTargetForLink(link); + if (!heading) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + window.history.pushState(null, "", `#${heading.id}`); + scrollToTocTarget(heading, "smooth"); + scheduleSync(); + }, true); + + window.addEventListener("scroll", scheduleSync, { passive: true }); + window.addEventListener("resize", scheduleSync); + window.addEventListener("hashchange", scheduleSync); + window.addEventListener("dstack:toc-update", scheduleSync); + scheduleSync(); +} + +function getTocItems(toc) { + return [...toc.querySelectorAll("a.md-nav__link[href*='#']")] + .map(link => { + const target = getTocTargetForLink(link); + return target && isVisibleTocTarget(target) ? { link, target } : null; + }) + .filter(Boolean); +} + +function getTocTargetForLink(link) { + if (!link) { + return null; + } + + const url = new URL(link.getAttribute("href"), window.location.href); + if ( + url.origin !== window.location.origin || + url.pathname !== window.location.pathname || + !url.hash + ) { + return null; + } + + return document.getElementById(decodeHashId(url.hash.slice(1))); +} + +function isVisibleTocTarget(target) { + const style = window.getComputedStyle(target); + const rect = target.getBoundingClientRect(); + return style.display !== "none" && style.visibility !== "hidden" && rect.height > 0; +} + +function getTocActivationTop() { + const header = document.querySelector(".md-header"); + const headerBottom = header?.getBoundingClientRect().bottom || 0; + return headerBottom + Math.min(120, window.innerHeight * 0.25); +} + +function scrollToTocTarget(target, behavior = "auto") { + const style = window.getComputedStyle(target); + const scrollMargin = Number.parseFloat(style.scrollMarginTop) || 0; + const top = target.getBoundingClientRect().top + window.scrollY - scrollMargin; + window.scrollTo({ top, behavior }); +} + +function decodeHashId(hashId) { + try { + return decodeURIComponent(hashId); + } catch { + return hashId; + } +} + window.addEventListener("DOMContentLoaded", function() { let tabs = document.querySelector(".md-tabs") let header = document.querySelector(".md-header") @@ -143,6 +373,7 @@ window.addEventListener("DOMContentLoaded", function() { header.classList.add("ready") setupTermynal() setupCustomCodeTitles() + setupSensitiveTocActiveState() }); (function () { diff --git a/mkdocs/assets/javascripts/swagger.js b/mkdocs/assets/javascripts/swagger.js new file mode 100644 index 0000000000..8138b41fee --- /dev/null +++ b/mkdocs/assets/javascripts/swagger.js @@ -0,0 +1,2685 @@ +(function () { + const SWAGGER_OPTIONS = { + defaultModelsExpandDepth: -1, + docExpansion: "full", + }; + const CURL_CONTINUATION_INDENT = " "; + + let swaggerCounter = 0; + + function initSwaggerReferences() { + document.querySelectorAll(".dstack-swagger-ui:not([data-swagger-mounted])").forEach((root) => { + initSwagger(root).catch((error) => { + root.dataset.swaggerMounted = "error"; + console.error("Failed to render Swagger UI", error); + }); + }); + } + + async function initSwagger(root) { + if (typeof SwaggerUIBundle !== "function") { + return; + } + const url = root.dataset.openapiUrl; + if (!url) { + return; + } + + root.dataset.swaggerMounted = "true"; + if (!root.id) { + swaggerCounter += 1; + root.id = `dstack-swagger-ui-${swaggerCounter}`; + } + + const spec = await fetch(url).then((response) => { + if (!response.ok) { + throw new Error(`Failed to load ${url}: ${response.status}`); + } + return response.json(); + }); + const swaggerSpec = root.dataset.openapiTag + ? filterSpecByTag(spec, root.dataset.openapiTag) + : spec; + stripSchemaTitles(swaggerSpec); + const referenceSpec = cloneJson(swaggerSpec); + + SwaggerUIBundle({ + spec: swaggerSpec, + dom_id: `#${root.id}`, + ...SWAGGER_OPTIONS, + }); + + setupSummaryToggleGuard(); + setupOperationAnchors(root); + setupOperationLayout(root, referenceSpec); + setupOperationTocScrolling(root); + setupRequestCurlExamples(root, referenceSpec); + setupSchemaNameBadges(root, referenceSpec); + setupModelPropertyLabels(root, referenceSpec); + setupAuthorizationDialogLabels(); + } + + function cloneJson(value) { + return JSON.parse(JSON.stringify(value)); + } + + function filterSpecByTag(spec, tagName) { + const paths = {}; + + Object.entries(spec.paths || {}).forEach(([path, pathItem]) => { + if (!pathItem || typeof pathItem !== "object") { + return; + } + + const filteredPathItem = {}; + let hasTaggedOperation = false; + + Object.entries(pathItem).forEach(([key, value]) => { + if (!isHttpMethod(key)) { + filteredPathItem[key] = value; + return; + } + if (!value || typeof value !== "object") { + return; + } + + const operationTags = Array.isArray(value.tags) && value.tags.length > 0 + ? value.tags + : ["default"]; + if (!operationTags.includes(tagName)) { + return; + } + + filteredPathItem[key] = { + ...value, + tags: [tagName], + }; + hasTaggedOperation = true; + }); + + if (hasTaggedOperation) { + paths[path] = filteredPathItem; + } + }); + + return { + ...spec, + tags: getFilteredTags(spec, tagName), + paths, + }; + } + + function getFilteredTags(spec, tagName) { + const tags = (spec.tags || []).filter((tag) => { + return tag && typeof tag === "object" && tag.name === tagName; + }); + return tags.length > 0 ? tags : [{ name: tagName }]; + } + + function isHttpMethod(key) { + return ["get", "put", "post", "delete", "options", "head", "patch", "trace"].includes( + key.toLowerCase() + ); + } + + function setupOperationAnchors(root) { + let scheduled = false; + const update = () => { + scheduled = false; + updateOperationAnchors(root); + }; + const scheduleUpdate = () => { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(update); + }; + + const observer = new MutationObserver(scheduleUpdate); + observer.observe(root, { + childList: true, + subtree: true, + }); + window.addEventListener("resize", scheduleUpdate); + scheduleUpdate(); + } + + function setupOperationLayout(root, spec) { + let scheduled = false; + const update = () => { + scheduled = false; + updateOperationLayout(root, spec); + }; + const scheduleUpdate = () => { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(update); + }; + + const observer = new MutationObserver(scheduleUpdate); + observer.observe(root, { + childList: true, + subtree: true, + }); + root.addEventListener("input", scheduleUpdate, true); + scheduleUpdate(); + } + + function updateOperationAnchors(root) { + const anchors = getOperationAnchors(root); + if (anchors.size === 0) { + return; + } + + root.querySelectorAll(".opblock").forEach((opblock) => { + const key = getOperationKeyForOpblock(opblock); + const anchor = key ? anchors.get(key) : null; + if (!anchor) { + return; + } + anchor.classList.add("dstack-swagger-operation-title"); + if (anchor.parentElement === opblock) { + return; + } + opblock.insertBefore(anchor, opblock.firstChild); + }); + + scrollToCurrentOperationHash(root); + window.dispatchEvent(new Event("dstack:toc-update")); + } + + function updateOperationLayout(root, spec) { + root.querySelectorAll(".opblock").forEach((opblock) => { + const operation = getOperationForOpblock(spec, opblock); + makeSummaryStatic(opblock); + moveOperationDescription(opblock); + setupOperationUrlCopy(opblock); + moveOperationHeaderActions(opblock); + updateParametersSectionState(opblock); + setupParameterInputPlaceholders(opblock, operation, spec); + setupParameterMetaLabels(opblock); + setupResponseBlocks(opblock, operation, spec); + setupRequestEditors(opblock, operation, spec); + setupTryOutCancelReset(opblock); + }); + } + + function setupRequestEditors(opblock, operation, spec) { + if (!operation) { + return; + } + renameRequestBodyEditTabs(opblock); + setupRequestBodyEditSchemaPanels(opblock, operation, spec); + opblock + .querySelectorAll(":scope .opblock-section-request-body textarea:not(.curl)") + .forEach((textarea) => { + const wrapper = textarea.closest(".body-param") || textarea.parentElement; + wrapper?.classList.add("dstack-editable-code", "dstack-swagger-request-editor"); + }); + } + + function renameRequestBodyEditTabs(opblock) { + opblock + .querySelectorAll(":scope .opblock-section-request-body .tablinks") + .forEach((button) => { + if (/^\s*edit value\s*$/i.test(button.textContent || "")) { + button.textContent = "Request Body"; + } else if (/^\s*schema\s*$/i.test(button.textContent || "")) { + button.textContent = "Request Body Schema"; + } + }); + } + + function setupRequestBodyEditSchemaPanels(opblock, operation, spec) { + const schema = getRequestJsonSchema(operation, opblock); + const modelExample = opblock.querySelector( + ":scope .opblock-section-request-body .model-example" + ); + if (!modelExample) { + return; + } + + modelExample.classList.remove("dstack-swagger-edit-schema-active"); + const editor = modelExample.querySelector(":scope > .dstack-swagger-edit-request-schema"); + if (editor) { + editor.hidden = true; + } + if (!schema || !isRequestBodyEditing(modelExample)) { + return; + } + const schemaTab = [...modelExample.querySelectorAll(".tablinks")].find((button) => { + return /request body schema|schema/i.test(button.textContent || ""); + }); + if (!schemaTab || !isSwaggerTabActive(schemaTab)) { + return; + } + + renderEditRequestBodySchemaPanel(modelExample, schema, spec); + } + + function isSwaggerTabActive(button) { + return ( + button.getAttribute("aria-selected") === "true" || + button.closest("li")?.classList.contains("active") || + button.classList.contains("active") + ); + } + + function renderEditRequestBodySchemaPanel(modelExample, schema, spec) { + const tab = modelExample.querySelector(":scope > .tab"); + let editor = modelExample.querySelector(":scope > .dstack-swagger-edit-request-schema"); + if (!editor) { + const container = document.createElement("div"); + container.innerHTML = getJsonEditorHtml( + "dstack-swagger-json-schema dstack-swagger-edit-request-schema" + ); + editor = container.firstElementChild; + } + + if (tab && editor.previousElementSibling !== tab) { + tab.after(editor); + } else if (!editor.parentElement) { + modelExample.appendChild(editor); + } + editor.hidden = false; + modelExample.classList.add("dstack-swagger-edit-schema-active"); + renderJsonSchemaPre(editor, schema, spec); + } + + function setupTryOutCancelReset(opblock) { + const resetButtons = getTryOutResetButtons(opblock); + resetButtons.forEach((button) => { + button.classList.add("dstack-swagger-hidden-reset"); + }); + opblock.querySelectorAll(".btn.execute").forEach((button) => { + const row = button.closest(".btn-group") || button.closest(".execute-wrapper"); + row?.classList.add("dstack-swagger-execute-row"); + row?.closest(".execute-wrapper")?.classList.add("dstack-swagger-execute-wrapper"); + }); + opblock.querySelectorAll(".execute-wrapper .btn, .btn-group .btn").forEach((button) => { + if (/^\s*clear\s*$/i.test(button.textContent || "")) { + button.classList.add("dstack-swagger-clear-btn"); + } + }); + + getTryOutCancelButtons(opblock).forEach((button) => { + if (button.dataset.dstackSwaggerCancelResets === "true") { + return; + } + button.dataset.dstackSwaggerCancelResets = "true"; + button.addEventListener("click", () => { + getTryOutResetButtons(opblock) + .find((resetButton) => resetButton !== button) + ?.click(); + }, true); + }); + } + + function getTryOutResetButtons(opblock) { + return [...opblock.querySelectorAll(".try-out__btn.reset, .try-out .btn.reset")] + .filter((button) => /reset/i.test(button.textContent || "")); + } + + function getTryOutCancelButtons(opblock) { + return [...opblock.querySelectorAll(".try-out__btn.cancel, .try-out .btn.cancel")] + .filter((button) => /cancel/i.test(button.textContent || "")); + } + + function makeSummaryStatic(opblock) { + const summary = opblock.querySelector(":scope > .opblock-summary"); + const control = summary?.querySelector(":scope > .opblock-summary-control"); + if (!control) { + return; + } + control.classList.add("dstack-swagger-summary-static"); + control.setAttribute("aria-expanded", "true"); + control.setAttribute("aria-disabled", "true"); + control.removeAttribute("tabindex"); + } + + function moveOperationDescription(opblock) { + const summary = opblock.querySelector(":scope > .opblock-summary"); + if (!summary) { + return; + } + + const bodyDescriptions = [ + ...opblock.querySelectorAll( + ":scope > .opblock-body > .opblock-description-wrapper, " + + ":scope > .no-margin > .opblock-body > .opblock-description-wrapper" + ), + ]; + const sourceDescription = bodyDescriptions[0]; + const description = opblock.querySelector( + ":scope > .dstack-swagger-operation-description" + ); + if (!sourceDescription) { + description?.remove(); + return; + } + + bodyDescriptions.forEach((item) => { + item.classList.add("dstack-swagger-source-operation-description"); + }); + const descriptionHtml = sourceDescription.innerHTML; + let visibleDescription = description; + if (!visibleDescription) { + visibleDescription = sourceDescription.cloneNode(false); + visibleDescription.classList.add("dstack-swagger-operation-description"); + visibleDescription.classList.remove("dstack-swagger-source-operation-description"); + } + if (visibleDescription.dataset.dstackSwaggerDescriptionHtml !== descriptionHtml) { + visibleDescription.innerHTML = descriptionHtml; + visibleDescription.dataset.dstackSwaggerDescriptionHtml = descriptionHtml; + } + if (visibleDescription.nextElementSibling === summary) { + return; + } + opblock.insertBefore(visibleDescription, summary); + } + + function setupSummaryToggleGuard() { + if (document.documentElement.dataset.dstackSwaggerSummaryGuard === "true") { + return; + } + document.documentElement.dataset.dstackSwaggerSummaryGuard = "true"; + + const preventSummaryToggle = (event) => { + const target = event.target instanceof Element ? event.target : event.target.parentElement; + if (!target?.closest(".dstack-swagger-ui .opblock > .opblock-summary")) { + return; + } + if (isAllowedSummaryInteraction(target)) { + return; + } + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + }; + + document.addEventListener("click", preventSummaryToggle, true); + document.addEventListener("keydown", (event) => { + if (event.key === "Enter" || event.key === " ") { + preventSummaryToggle(event); + } + }, true); + } + + function setupAuthorizationDialogLabels() { + if (document.documentElement.dataset.dstackSwaggerAuthLabels === "true") { + return; + } + document.documentElement.dataset.dstackSwaggerAuthLabels = "true"; + + let scheduled = false; + const scheduleUpdate = () => { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(() => { + scheduled = false; + updateAuthorizationDialogLabels(); + }); + }; + + const observer = new MutationObserver(scheduleUpdate); + observer.observe(document.body, { + childList: true, + subtree: true, + }); + scheduleUpdate(); + } + + function updateAuthorizationDialogLabels() { + document + .querySelectorAll( + ".dstack-swagger-ui .swagger-ui .auth-container " + + ":is(label, span, p, div)" + ) + .forEach((element) => { + if (element.childNodes.length !== 1) { + return; + } + if ((element.textContent || "").trim() === "Value:") { + element.textContent = "User token:"; + } + }); + } + + function isAllowedSummaryInteraction(target) { + if (!target) { + return false; + } + if (target.closest(".dstack-swagger-url-copy")) { + return true; + } + if ( + target.closest( + ".dstack-swagger-summary-url .opblock-summary-path, " + + ".dstack-swagger-summary-url .opblock-summary-path__deprecated" + ) + ) { + return true; + } + if (!target.closest(".dstack-swagger-summary-actions")) { + return false; + } + return Boolean(target.closest("a, button, input, select, textarea, [role='button']")); + } + + function setupOperationUrlCopy(opblock) { + const wrapper = opblock.querySelector( + ":scope > .opblock-summary .opblock-summary-path-description-wrapper" + ); + const path = wrapper?.querySelector( + ":scope .opblock-summary-path, :scope .opblock-summary-path__deprecated" + ); + if (!wrapper || !path) { + return; + } + + wrapper.classList.add("dstack-swagger-summary-url"); + path.removeAttribute("role"); + path.removeAttribute("tabindex"); + path.removeAttribute("aria-label"); + path.removeAttribute("title"); + if (path.dataset.dstackSwaggerSelectablePath !== "true") { + path.addEventListener("click", stopSummaryControlPropagation); + path.addEventListener("mousedown", stopSummaryControlPropagation); + path.addEventListener("touchstart", stopSummaryControlPropagation); + path.dataset.dstackSwaggerSelectablePath = "true"; + } + + let copyButton = wrapper.querySelector(":scope > .dstack-swagger-url-copy"); + if (!copyButton || copyButton.tagName.toLowerCase() !== "span") { + const nextCopyButton = document.createElement("span"); + if (copyButton) { + copyButton.replaceWith(nextCopyButton); + } + copyButton = nextCopyButton; + copyButton.className = "dstack-swagger-url-copy"; + copyButton.title = "Copy URL"; + copyButton.setAttribute("aria-label", "Copy URL"); + copyButton.setAttribute("role", "button"); + wrapper.appendChild(copyButton); + } + + if (copyButton.dataset.dstackSwaggerUrlCopy === "true") { + return; + } + copyButton.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + copyOperationUrl(opblock, copyButton); + }); + copyButton.addEventListener("keydown", (event) => { + if (event.key !== "Enter" && event.key !== " ") { + return; + } + event.preventDefault(); + event.stopPropagation(); + copyOperationUrl(opblock, copyButton); + }); + copyButton.dataset.dstackSwaggerUrlCopy = "true"; + } + + function copyOperationUrl(opblock, copyButton) { + copyText(getOperationUrl(opblock)).then(() => showCopiedHint(copyButton)); + } + + function showCopiedHint(copyButton) { + if (!copyButton) { + return; + } + copyButton.classList.add("dstack-swagger-url-copy-copied"); + window.clearTimeout(Number(copyButton.dataset.dstackSwaggerCopiedTimeout || 0)); + const timeout = window.setTimeout(() => { + copyButton.classList.remove("dstack-swagger-url-copy-copied"); + delete copyButton.dataset.dstackSwaggerCopiedTimeout; + }, 1300); + copyButton.dataset.dstackSwaggerCopiedTimeout = String(timeout); + } + + function getOperationUrl(opblock) { + const path = opblock.querySelector(".opblock-summary-path")?.dataset.path || ""; + const serverUrl = opblock + .closest(".dstack-swagger-ui") + ?.querySelector(".scheme-container .servers select")?.value; + if (!serverUrl || !path) { + return path; + } + return `${serverUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`; + } + + function copyText(text) { + if (!text) { + return Promise.resolve(); + } + if (navigator.clipboard?.writeText) { + return navigator.clipboard.writeText(text).catch(() => copyTextFallback(text)); + } + return copyTextFallback(text); + } + + function copyTextFallback(text) { + const input = document.createElement("textarea"); + input.value = text; + input.setAttribute("readonly", ""); + input.style.position = "fixed"; + input.style.top = "-1000px"; + document.body.appendChild(input); + input.select(); + document.execCommand("copy"); + input.remove(); + return Promise.resolve(); + } + + function moveOperationHeaderActions(opblock) { + const summary = opblock.querySelector(":scope > .opblock-summary"); + if (!summary) { + return; + } + + const actions = getSummaryActions(summary); + moveRequestContentType(opblock, actions); + moveTryOutButton(opblock, actions); + } + + function moveRequestContentType(opblock, actions) { + const requestBodyHeader = opblock.querySelector( + ":scope .opblock-section-request-body > .opblock-section-header" + ); + const headerContentType = requestBodyHeader?.querySelector( + ":scope .content-type-wrapper" + ); + actions + .querySelectorAll(":scope > .content-type-wrapper:not(.dstack-swagger-content-type-proxy)") + .forEach((contentType) => contentType.remove()); + if (!headerContentType) { + actions.querySelector(":scope > .dstack-swagger-content-type-proxy")?.remove(); + requestBodyHeader?.classList.remove("dstack-swagger-request-body-header-hidden"); + return; + } + + requestBodyHeader?.classList.add("dstack-swagger-request-body-header-hidden"); + headerContentType.classList.add("dstack-swagger-original-control"); + + const contentType = getRequestContentTypeProxy(actions, headerContentType); + if (contentType.parentElement !== actions) { + actions.appendChild(contentType); + } + } + + function moveTryOutButton(opblock, actions) { + const headerButtons = [ + ...opblock.querySelectorAll(":scope .opblock-section-header .try-out__btn"), + ]; + const headerButton = headerButtons[0]; + actions + .querySelectorAll(":scope > .try-out__btn, :scope > .dstack-swagger-try-out-proxy") + .forEach((button) => button.remove()); + opblock + .querySelectorAll(".opblock-section-header.dstack-swagger-try-out-source-header") + .forEach((header) => { + if (!header.contains(headerButton)) { + header.classList.remove("dstack-swagger-try-out-source-header"); + } + }); + if (!headerButton) { + opblock.classList.remove("dstack-swagger-has-try-out"); + opblock.style.removeProperty("--dstack-swagger-try-out-top"); + opblock.style.removeProperty("--dstack-swagger-try-out-width"); + actions.querySelector(":scope > .dstack-swagger-try-out-proxy")?.remove(); + return; + } + + headerButtons.forEach((button) => { + button.classList.remove("dstack-swagger-original-control"); + button.classList.add("dstack-swagger-summary-try-out"); + }); + headerButton.closest(".opblock-section-header")?.classList.add( + "dstack-swagger-try-out-source-header" + ); + opblock.classList.add("dstack-swagger-has-try-out"); + positionTryOutButtons(opblock, headerButtons); + } + + function getSummaryActions(summary) { + let actions = summary.querySelector(":scope > .dstack-swagger-summary-actions"); + if (!actions) { + actions = document.createElement("div"); + actions.className = "dstack-swagger-summary-actions"; + summary.appendChild(actions); + } + return actions; + } + + function positionTryOutButtons(opblock, buttons) { + const summary = opblock.querySelector(":scope > .opblock-summary"); + if (!summary) { + return; + } + + const top = + summary.offsetTop + + Math.max(0, (summary.offsetHeight - buttons[0].offsetHeight) / 2); + let right = 0; + [...buttons].reverse().forEach((button) => { + button.style.setProperty("right", `${right}px`); + button.style.setProperty("top", `${top}px`); + right += button.offsetWidth + 8; + }); + const width = Math.max(0, right - 8); + opblock.style.setProperty("--dstack-swagger-try-out-top", `${top}px`); + opblock.style.setProperty("--dstack-swagger-try-out-width", `${width}px`); + } + + function getRequestContentTypeProxy(actions, sourceContentType) { + let proxy = actions.querySelector(":scope > .dstack-swagger-content-type-proxy"); + if (!proxy) { + proxy = sourceContentType.cloneNode(true); + proxy.addEventListener("change", syncRequestContentTypeProxy); + proxy.addEventListener("input", syncRequestContentTypeProxy); + proxy.addEventListener("click", stopSummaryControlPropagation); + proxy.addEventListener("keydown", stopSummaryControlPropagation); + } else if (proxy.dataset.dstackSwaggerSourceHtml !== sourceContentType.innerHTML) { + proxy.innerHTML = sourceContentType.innerHTML; + } + proxy.className = sourceContentType.className; + proxy.classList.remove("dstack-swagger-original-control"); + proxy.classList.add( + "dstack-swagger-summary-content-type", + "dstack-swagger-content-type-proxy" + ); + proxy.dataset.dstackSwaggerSourceHtml = sourceContentType.innerHTML; + + const sourceSelect = sourceContentType.querySelector("select"); + const proxySelect = proxy.querySelector("select"); + if (sourceSelect && proxySelect) { + proxySelect.value = sourceSelect.value; + proxySelect.disabled = sourceSelect.disabled; + } + return proxy; + } + + function syncRequestContentTypeProxy(event) { + const proxySelect = event.target.closest("select"); + const opblock = proxySelect?.closest(".opblock"); + const sourceSelect = opblock?.querySelector( + ":scope .opblock-section-request-body > .opblock-section-header " + + ".content-type-wrapper:not(.dstack-swagger-content-type-proxy) select" + ); + if (!proxySelect || !sourceSelect || sourceSelect.value === proxySelect.value) { + return; + } + sourceSelect.value = proxySelect.value; + sourceSelect.dispatchEvent(new Event("input", { bubbles: true })); + sourceSelect.dispatchEvent(new Event("change", { bubbles: true })); + } + + function stopSummaryControlPropagation(event) { + event.stopPropagation(); + } + + function updateParametersSectionState(opblock) { + opblock.querySelectorAll(".opblock-section-header").forEach((header) => { + const title = (header.querySelector("h4")?.textContent || "").trim(); + const container = header.nextElementSibling; + if (title !== "Parameters" || !container?.classList.contains("parameters-container")) { + return; + } + + const isEmpty = container.querySelectorAll(".parameters tbody tr").length === 0; + header.classList.toggle("dstack-swagger-empty-parameters", isEmpty); + container.classList.toggle("dstack-swagger-empty-parameters", isEmpty); + }); + } + + function setupParameterInputPlaceholders(opblock, operation, spec) { + const parameters = operation ? getOperationParameters(opblock, operation, spec) : []; + opblock.querySelectorAll(":scope .parameters tbody tr").forEach((row) => { + const placeholder = getParameterInputPlaceholder(row, parameters); + row.querySelectorAll( + "input:not([type='checkbox']):not([type='radio']):not([type='file']), textarea" + ).forEach((control) => { + setupParameterInputDirtyTracking(control); + clearAutoFilledParameterDefault(control, placeholder); + if (control.dataset.dstackSwaggerParameterPlaceholder === placeholder) { + return; + } + control.setAttribute("placeholder", placeholder); + control.dataset.dstackSwaggerParameterPlaceholder = placeholder; + }); + }); + } + + function getParameterInputPlaceholder(row, parameters) { + const defaultValue = getParameterDefaultValue(row, parameters); + if (defaultValue !== null) { + return `defaults to ${defaultValue}`; + } + return row.querySelector(".parameter__name.required") ? "required" : "optional"; + } + + function getParameterDefaultValue(row, parameters) { + const parameter = getParameterForRow(row, parameters); + if (parameter?.schema?.default !== undefined) { + return formatParameterDefaultValue(parameter.schema.default); + } + const text = (row.querySelector(".parameter__default")?.textContent || "") + .replace(/\s+/g, " ") + .trim(); + const defaultValue = text.replace(/^Default value\s*:\s*/i, "").trim(); + return defaultValue ? defaultValue : null; + } + + function getParameterForRow(row, parameters) { + const name = getParameterRowName(row); + const location = getParameterRowLocation(row); + if (!name) { + return null; + } + return parameters.find((parameter) => { + return parameter.name === name && (!location || parameter.in === location); + }) || null; + } + + function getParameterRowName(row) { + const element = row.querySelector(".parameter__name"); + if (!element) { + return ""; + } + for (const node of element.childNodes) { + if (node.nodeType === Node.TEXT_NODE) { + const text = node.textContent.trim(); + if (text) { + return text; + } + } + } + return (element.textContent || "") + .replace(/\brequired\b/gi, "") + .replace(/\*+$/, "") + .trim(); + } + + function getParameterRowLocation(row) { + return (row.querySelector(".parameter__in")?.textContent || "") + .trim() + .replace(/^\((.*)\)$/, "$1"); + } + + function formatParameterDefaultValue(value) { + if (typeof value === "string") { + return value === "" ? '""' : value; + } + return JSON.stringify(value); + } + + function setupParameterInputDirtyTracking(control) { + if (control.dataset.dstackSwaggerParameterDirtyTracking === "true") { + return; + } + control.dataset.dstackSwaggerParameterDirtyTracking = "true"; + control.addEventListener("input", () => { + control.dataset.dstackSwaggerParameterDirty = "true"; + }); + } + + function clearAutoFilledParameterDefault(control, placeholder) { + const defaultValue = placeholder.replace(/^defaults to\s+/i, "").trim(); + if ( + defaultValue === placeholder || + control.dataset.dstackSwaggerParameterDirty === "true" || + String(control.value || "").trim() !== defaultValue + ) { + return; + } + control.value = ""; + } + + function setupParameterMetaLabels(opblock) { + opblock.querySelectorAll(":scope .parameter__in").forEach((element) => { + const text = (element.textContent || "").trim(); + const normalized = text.replace(/^\((.*)\)$/, "$1"); + if (normalized && normalized !== text) { + element.textContent = normalized; + } + }); + } + + function setupResponseBlocks(opblock, operation, spec) { + const wrapper = opblock.querySelector(":scope .responses-wrapper"); + if (!wrapper) { + return; + } + wrapper.classList.add("dstack-swagger-responses"); + hideNativeResponseCaptions(wrapper); + wrapper.querySelectorAll(".responses-table tbody tr.response").forEach((row) => { + const code = getResponseCode(row); + const response = getResponseForCode(operation, code); + const schema = getContentSchema(response?.content); + setupResponseBlock( + row, + row.closest(".live-responses-table") !== null, + response, + schema, + spec + ); + }); + } + + function hideNativeResponseCaptions(wrapper) { + wrapper.querySelectorAll("h4, h5, .opblock-section-header").forEach((element) => { + const text = (element.textContent || "").trim().toLowerCase(); + if (text === "server response" || text === "responses") { + element.classList.add("dstack-swagger-native-response-caption"); + } + }); + } + + function setupResponseBlock(row, isLiveResponse, response, schema, spec) { + const descriptionCell = row.querySelector(":scope > .response-col_description"); + if (!descriptionCell) { + return; + } + + const code = getResponseCode(row); + const isSuccess = isSuccessResponseCode(code); + row.classList.add("dstack-swagger-response-block"); + row.classList.toggle("dstack-swagger-response-success", !isLiveResponse && isSuccess); + row.classList.toggle("dstack-swagger-response-collapsible", !isLiveResponse && !isSuccess); + row.classList.toggle("dstack-swagger-live-response", isLiveResponse); + + const description = + row.dataset.dstackSwaggerResponseDescription || getResponseDescription(descriptionCell); + row.dataset.dstackSwaggerResponseDescription = description; + const container = getResponseContainer(descriptionCell, isSuccess, isLiveResponse); + updateResponseContainerTitle(container, code, description, isSuccess, isLiveResponse); + const body = getResponseContainerBody(container); + + [...descriptionCell.childNodes].forEach((node) => { + if (node !== container) { + body.appendChild(node); + } + }); + body.querySelector(":scope > .response-col_description__inner")?.remove(); + setupResponseExampleSection(body, response, schema, spec, isLiveResponse); + } + + function getResponseForCode(operation, code) { + if (!operation) { + return null; + } + return operation.responses?.[code] || operation.responses?.default || null; + } + + function setupResponseExampleSection(body, response, schema, spec, isLiveResponse) { + if (isLiveResponse) { + body.querySelector(":scope > .dstack-swagger-response-example")?.remove(); + showResponseSources(body); + return; + } + + const exampleText = getResponseExampleText(body, response, schema, spec); + const hasExample = Boolean(exampleText); + const hasSchema = Boolean(schema && !isEmptySchema(schema)); + if (!hasExample && !hasSchema) { + body.querySelector(":scope > .dstack-swagger-response-example")?.remove(); + showResponseSources(body); + return; + } + + hideResponseSources(body); + const wrapper = ensureResponseExampleWrapper(body, hasExample, hasSchema); + if (hasExample) { + renderJsonEditorPre( + wrapper.querySelector(".dstack-swagger-response-json-example"), + exampleText + ); + } + if (hasSchema) { + renderJsonSchemaPre(wrapper.querySelector(".dstack-swagger-response-json-schema"), schema, spec); + } + syncResponseExampleMode(wrapper); + } + + function hideResponseSources(body) { + body.querySelectorAll( + ".response-controls, .model-example, .model-box, .json-schema-2020-12, " + + ".highlight-code" + ).forEach((element) => { + element.classList.add("dstack-swagger-response-source"); + }); + } + + function showResponseSources(body) { + body.querySelectorAll(".dstack-swagger-response-source") + .forEach((element) => { + element.classList.remove("dstack-swagger-response-source"); + }); + } + + function ensureResponseExampleWrapper(body, hasExample, hasSchema) { + const signature = `${hasExample ? "example" : ""}:${hasSchema ? "schema" : ""}`; + let wrapper = body.querySelector(":scope > .dstack-swagger-response-example"); + if (wrapper?.dataset.dstackSwaggerResponsePanels === signature) { + return wrapper; + } + + if (!wrapper) { + wrapper = document.createElement("div"); + wrapper.className = "dstack-swagger-response-example"; + body.appendChild(wrapper); + } + wrapper.dataset.dstackSwaggerResponsePanels = signature; + const tabs = hasExample && hasSchema + ? ` + + ` + : ""; + wrapper.innerHTML = ` + ${tabs} + ${hasExample ? ` +
+ ${getJsonEditorHtml("dstack-swagger-response-json-example")} +
+ ` : ""} + ${hasSchema ? ` +
+ ${getJsonEditorHtml("dstack-swagger-json-schema dstack-swagger-response-json-schema")} +
+ ` : ""} + `; + + wrapper + .querySelector(".dstack-swagger-response-example-tab") + ?.addEventListener("click", () => setResponseExampleMode(wrapper, "example")); + wrapper + .querySelector(".dstack-swagger-response-schema-tab") + ?.addEventListener("click", () => setResponseExampleMode(wrapper, "schema")); + return wrapper; + } + + function setResponseExampleMode(wrapper, mode) { + wrapper.dataset.dstackSwaggerResponseExampleMode = mode; + syncResponseExampleMode(wrapper); + } + + function syncResponseExampleMode(wrapper) { + const examplePanel = wrapper.querySelector(".dstack-swagger-response-example-panel"); + const schemaPanel = wrapper.querySelector(".dstack-swagger-response-schema-panel"); + const mode = wrapper.dataset.dstackSwaggerResponseExampleMode || + (examplePanel ? "example" : "schema"); + const isSchema = mode === "schema" && Boolean(schemaPanel); + + if (examplePanel) { + examplePanel.hidden = isSchema; + } + if (schemaPanel) { + schemaPanel.hidden = !isSchema; + } + wrapper.querySelectorAll(".tab li").forEach((item) => item.classList.remove("active")); + wrapper.querySelectorAll(".tablinks").forEach((button) => { + const selected = + (isSchema && button.classList.contains("dstack-swagger-response-schema-tab")) || + (!isSchema && button.classList.contains("dstack-swagger-response-example-tab")); + button.setAttribute("aria-selected", selected ? "true" : "false"); + button.closest("li")?.classList.toggle("active", selected); + }); + + } + + function getResponseExampleText(body, response, schema, spec) { + return ( + getResponseExampleTextFromSpec(response) || + getResponseExampleTextFromDom(body) || + getResponseExampleTextFromSchema(schema, spec) + ); + } + + function getResponseExampleTextFromSpec(response) { + const media = getResponseMedia(response); + if (!media) { + return ""; + } + if (media.example !== undefined) { + return stringifyJsonValue(media.example); + } + + const firstExample = Object.values(media.examples || {})[0]; + if (firstExample?.value !== undefined) { + return stringifyJsonValue(firstExample.value); + } + return ""; + } + + function getResponseMedia(response) { + return Object.values(response?.content || {})[0] || null; + } + + function getResponseExampleTextFromDom(body) { + const candidates = [ + ...body.querySelectorAll( + ".model-example .highlight-code pre, .model-example .highlight-code code, " + + ".highlight-code pre, .highlight-code code" + ), + ]; + for (const candidate of candidates) { + if (candidate.closest(".dstack-swagger-response-example")) { + continue; + } + + const text = normalizeJsonText(candidate.textContent || ""); + if (text && !/^(example value|schema)$/i.test(text)) { + return text; + } + } + return ""; + } + + function getResponseExampleTextFromSchema(schema, spec) { + if (!schema || isEmptySchema(schema)) { + return ""; + } + const example = buildSchemaExample(schema, spec); + return example === undefined ? "" : stringifyJsonValue(example); + } + + function buildSchemaExample(schema, spec, seenRefs = new Set(), depth = 0) { + if (!schema || typeof schema !== "object") { + return undefined; + } + if (depth > 8) { + return null; + } + + if (schema.example !== undefined) { + return schema.example; + } + if (schema.default !== undefined) { + return schema.default; + } + if (Array.isArray(schema.enum) && schema.enum.length > 0) { + return schema.enum[0]; + } + if (schema.const !== undefined) { + return schema.const; + } + + const refName = getRefName(schema); + if (refName) { + if (seenRefs.has(refName)) { + return {}; + } + seenRefs.add(refName); + return buildSchemaExample(resolveSchema(schema, spec), spec, seenRefs, depth + 1); + } + + if (Array.isArray(schema.allOf) && schema.allOf.length > 0) { + return mergeSchemaExamples( + schema.allOf.map((item) => buildSchemaExample(item, spec, seenRefs, depth + 1)) + ); + } + + const union = schema.oneOf || schema.anyOf; + if (Array.isArray(union) && union.length > 0) { + return buildSchemaExample(union[0], spec, seenRefs, depth + 1); + } + + const type = getSchemaExampleType(schema); + if (type === "array") { + return [buildSchemaExample(schema.items || {}, spec, seenRefs, depth + 1)]; + } + if (type === "object") { + return buildObjectSchemaExample(schema, spec, seenRefs, depth); + } + if (type === "integer" || type === "number") { + return typeof schema.minimum === "number" ? schema.minimum : 0; + } + if (type === "boolean") { + return false; + } + if (type === "null") { + return null; + } + return "string"; + } + + function mergeSchemaExamples(examples) { + const definedExamples = examples.filter((example) => example !== undefined); + if (definedExamples.every(isPlainObject)) { + return definedExamples.reduce((merged, example) => ({ ...merged, ...example }), {}); + } + return definedExamples[definedExamples.length - 1]; + } + + function buildObjectSchemaExample(schema, spec, seenRefs, depth) { + const properties = getSchemaProperties(schema, spec) || schema.properties; + if (properties && Object.keys(properties).length > 0) { + return Object.fromEntries( + Object.entries(properties).map(([name, propertySchema]) => [ + name, + buildSchemaExample(propertySchema, spec, seenRefs, depth + 1), + ]) + ); + } + if (schema.additionalProperties && typeof schema.additionalProperties === "object") { + return { + additionalProp1: buildSchemaExample( + schema.additionalProperties, + spec, + seenRefs, + depth + 1 + ), + }; + } + return {}; + } + + function getSchemaExampleType(schema) { + if (Array.isArray(schema.type)) { + return schema.type.find((type) => type !== "null") || "null"; + } + if (schema.type) { + return schema.type; + } + if (schema.properties || schema.additionalProperties) { + return "object"; + } + if (schema.items) { + return "array"; + } + return "string"; + } + + function isPlainObject(value) { + return Boolean(value) && typeof value === "object" && !Array.isArray(value); + } + + function getResponseCode(row) { + return ( + row.dataset.code || + row.querySelector(":scope > .response-col_status")?.textContent || + "" + ).trim(); + } + + function isSuccessResponseCode(code) { + return /^2\d\d$/.test(code); + } + + function getResponseContainer(descriptionCell, isSuccess, isLiveResponse) { + const tagName = isLiveResponse ? "blockquote" : "details"; + let container = descriptionCell.querySelector( + ":scope > .dstack-swagger-response-container" + ); + if (!container) { + container = descriptionCell.querySelector( + ":scope > .dstack-swagger-response-admonition" + ); + } + if (container?.tagName.toLowerCase() === tagName) { + updateResponseContainerClass(container, isSuccess, isLiveResponse); + return container; + } + + const nextContainer = document.createElement(tagName); + updateResponseContainerClass(nextContainer, isSuccess, isLiveResponse); + if (container) { + while (container.firstChild) { + nextContainer.appendChild(container.firstChild); + } + container.replaceWith(nextContainer); + } else { + descriptionCell.prepend(nextContainer); + } + return nextContainer; + } + + function updateResponseContainerClass(container, isSuccess, isLiveResponse) { + container.className = isLiveResponse + ? "dstack-swagger-response-container dstack-swagger-response-section dstack-swagger-live-response" + : "dstack-swagger-response-container info dstack-swagger-response-admonition dstack-swagger-response-section"; + } + + function updateResponseContainerTitle(container, code, description, isSuccess, isLiveResponse) { + const tagName = isLiveResponse ? "h4" : "summary"; + let title = container.querySelector(":scope > .dstack-swagger-response-title"); + if (title?.tagName.toLowerCase() !== tagName) { + const nextTitle = document.createElement(tagName); + if (title) { + title.replaceWith(nextTitle); + } else { + container.prepend(nextTitle); + } + title = nextTitle; + } + title.className = "dstack-swagger-response-title"; + title.textContent = `${code || "default"} ${description || "Response"}`; + } + + function getResponseContainerBody(container) { + let body = container.querySelector(":scope > .dstack-swagger-response-body"); + if (!body) { + body = document.createElement("div"); + body.className = "dstack-swagger-response-body"; + container.appendChild(body); + } + const title = container.querySelector(":scope > .dstack-swagger-response-title"); + if (title && title.nextElementSibling !== body) { + title.after(body); + } + return body; + } + + function getResponseDescription(descriptionCell) { + return ( + descriptionCell.querySelector( + ".response-col_description__inner .renderedMarkdown, " + + ".response-col_description__inner" + )?.textContent || + "" + ).trim(); + } + + function setupRequestCurlExamples(root, spec) { + let scheduled = false; + const update = () => { + scheduled = false; + updateRequestCurlExamples(root, spec); + }; + const scheduleUpdate = () => { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(update); + }; + + const observer = new MutationObserver(scheduleUpdate); + observer.observe(root, { + childList: true, + subtree: true, + }); + scheduleUpdate(); + } + + function updateRequestCurlExamples(root, spec) { + root.querySelectorAll(".opblock").forEach((opblock) => { + const operation = getOperationForOpblock(spec, opblock); + if (!operation) { + return; + } + const modelExample = opblock.querySelector( + ":scope .opblock-section-request-body .model-example" + ); + if (!modelExample) { + teardownEditRequestCurlExample(opblock); + if (!operation.requestBody) { + setupOperationCurlExample(opblock, operation, spec); + } + return; + } + teardownOperationCurlExample(opblock); + if (isRequestBodyEditing(modelExample)) { + setupRequestCurlExample(opblock, modelExample, operation, spec, true); + return; + } + setupRequestCurlExample(opblock, modelExample, operation, spec, false); + }); + } + + function isRequestBodyEditing(modelExample) { + return Boolean( + modelExample.querySelector("textarea, .body-param__text") || + [...modelExample.querySelectorAll(".tablinks")].some((button) => + /^\s*(edit value|request body)\s*$/i.test(button.textContent || "") + ) + ); + } + + function teardownRequestCurlExample(modelExample) { + const wrapper = getRequestCurlWrapper(modelExample); + wrapper?.remove(); + modelExample.classList.remove("dstack-swagger-request-model-hidden"); + } + + function setupRequestCurlExample(opblock, modelExample, operation, spec, isEditing) { + const schema = getRequestJsonSchema(operation, opblock); + if (!schema) { + teardownRequestCurlExample(modelExample); + teardownEditRequestCurlExample(opblock); + return; + } + + if (isEditing) { + teardownRequestCurlExample(modelExample); + teardownEditRequestCurlExample(opblock); + return; + } + + const wrapper = ensureRequestCurlWrapper(modelExample); + wrapper.classList.remove("dstack-swagger-request-example-editing"); + teardownEditRequestCurlExample(opblock); + const body = getRequestExampleBody(modelExample, operation, opblock); + const curl = buildCurlCommand(opblock, operation, spec, body); + renderRequestCurlTermy(wrapper, curl); + renderRequestJsonSchema(wrapper, schema, spec); + + if (!wrapper.dataset.dstackSwaggerRequestExampleMode) { + setRequestExampleMode(modelExample, "curl"); + } else { + syncRequestExampleMode(modelExample); + } + } + + function setupOperationCurlExample(opblock, operation, spec) { + const wrapper = ensureOperationCurlWrapper(opblock); + const curl = buildCurlCommand(opblock, operation, spec, ""); + renderRequestCurlTermy(wrapper, curl); + } + + function ensureOperationCurlWrapper(opblock) { + let wrapper = opblock.querySelector(":scope > .dstack-swagger-operation-curl-example"); + if (wrapper) { + return wrapper; + } + + wrapper = document.createElement("div"); + wrapper.className = "dstack-swagger-operation-curl-example"; + wrapper.innerHTML = ` +
+
+
+ `; + opblock.querySelector(":scope > .opblock-summary")?.after(wrapper); + return wrapper; + } + + function teardownOperationCurlExample(opblock) { + opblock.querySelector(":scope > .dstack-swagger-operation-curl-example")?.remove(); + } + + function getRequestCurlWrapper(modelExample) { + const previous = modelExample.previousElementSibling; + return previous?.classList.contains("dstack-swagger-request-example") + ? previous + : null; + } + + function ensureRequestCurlWrapper(modelExample) { + let wrapper = getRequestCurlWrapper(modelExample); + if (wrapper) { + return wrapper; + } + + wrapper = document.createElement("div"); + wrapper.className = "dstack-swagger-request-example"; + wrapper.innerHTML = ` + +
+
+
+
+
+ + `; + wrapper + .querySelector(".dstack-swagger-request-curl-tab") + .addEventListener("click", () => setRequestExampleMode(modelExample, "curl")); + wrapper + .querySelector(".dstack-swagger-request-schema-tab") + .addEventListener("click", () => setRequestExampleMode(modelExample, "schema")); + modelExample.before(wrapper); + return wrapper; + } + + function renderRequestCurlTermy(wrapper, curl) { + const termy = wrapper.querySelector(".dstack-swagger-request-curl-termy"); + if (!termy || termy.dataset.dstackSwaggerCurl === curl) { + return; + } + + termy.dataset.termynalCopy = "true"; + termy.dataset.termynalInstant = "true"; + termy.dataset.termynalMaxHeight = "calc(var(--dstack-swagger-curl-max-height) - 90px)"; + termy.dstackTermynalCopyText = curl; + termy.innerHTML = '
'; + const highlight = termy.querySelector(".highlight"); + highlight.dataset.termynalSingleInput = "true"; + termy.querySelector("code").textContent = `$ ${curl}`; + termy.dataset.dstackSwaggerCurl = curl; + if (typeof setupTermynal === "function") { + setupTermynal(termy); + } + } + + function renderRequestJsonSchema(wrapper, schema, spec) { + const pre = wrapper.querySelector(".dstack-swagger-request-json-schema"); + renderJsonSchemaPre(pre, schema, spec); + } + + function getJsonEditorHtml(className) { + return ` +
+
+
+ `; + } + + function renderJsonSchemaPre(pre, schema, spec) { + const schemaDocument = buildJsonSchemaDocument(schema, spec); + const schemaText = JSON.stringify(orderJsonSchemaKeys(schemaDocument), null, 2); + renderJsonEditorPre(pre, schemaText); + } + + function renderJsonEditorPre(editor, text) { + const code = editor?.querySelector(":scope > pre > code, :scope > code"); + if (!editor || !code) { + return; + } + + const jsonText = normalizeJsonText(text); + setupJsonEditorCopyButton(editor, jsonText); + if (code.dataset.dstackSwaggerJson === jsonText) { + return; + } + + code.innerHTML = highlightJson(jsonText); + code.dataset.dstackSwaggerJson = jsonText; + } + + function setupJsonEditorCopyButton(pre, text) { + let nav = pre.querySelector(":scope > .md-code__nav"); + if (!nav) { + nav = document.createElement("nav"); + nav.className = "md-code__nav"; + pre.insertBefore(nav, pre.firstChild); + } + + let button = nav.querySelector(":scope > .md-code__button[data-md-type='copy']"); + if (!button) { + button = document.createElement("button"); + button.className = "md-code__button"; + button.type = "button"; + button.title = "Copy to clipboard"; + button.dataset.mdType = "copy"; + nav.appendChild(button); + } + button.dataset.clipboardText = text; + } + + function normalizeJsonText(value) { + const text = String(value || "").trim(); + if (!text) { + return ""; + } + try { + return JSON.stringify(JSON.parse(text), null, 2); + } catch { + return text; + } + } + + function stringifyJsonValue(value) { + if (typeof value === "string") { + return JSON.stringify(value, null, 2); + } + return JSON.stringify(value, null, 2); + } + + function highlightJson(json) { + return json.replace( + /("(?:\\u[a-fA-F0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, + (token) => { + const escaped = escapeHtml(token); + if (token.startsWith('"')) { + const className = token.endsWith(":") ? "nt" : "s2"; + return `${escaped}`; + } + if (/true|false|null/.test(token)) { + return `${escaped}`; + } + const className = /[.eE]/.test(token) ? "mf" : "mi"; + return `${escaped}`; + } + ); + } + + function escapeHtml(value) { + return String(value) + .replace(/&/g, "&") + .replace(//g, ">"); + } + + function setRequestExampleMode(modelExample, mode) { + const wrapper = getRequestCurlWrapper(modelExample); + if (!wrapper) { + return; + } + + wrapper.dataset.dstackSwaggerRequestExampleMode = mode; + syncRequestExampleMode(modelExample); + } + + function syncRequestExampleMode(modelExample) { + const wrapper = getRequestCurlWrapper(modelExample); + if (!wrapper) { + return; + } + + const mode = wrapper.dataset.dstackSwaggerRequestExampleMode || "curl"; + const isSchema = mode === "schema"; + const isEditing = wrapper.classList.contains("dstack-swagger-request-example-editing"); + wrapper.querySelector(".dstack-swagger-request-curl-panel").hidden = isSchema; + wrapper.querySelector(".dstack-swagger-request-schema-panel").hidden = !isSchema; + wrapper.querySelector(".dstack-swagger-request-curl-tab").closest("li").hidden = isEditing; + modelExample.classList.toggle("dstack-swagger-request-model-hidden", !isEditing); + + wrapper.querySelectorAll(".tab li").forEach((item) => item.classList.remove("active")); + wrapper.querySelectorAll(".tablinks").forEach((button) => { + const selected = + (isSchema && button.classList.contains("dstack-swagger-request-schema-tab")) || + (!isSchema && button.classList.contains("dstack-swagger-request-curl-tab")); + button.setAttribute("aria-selected", selected ? "true" : "false"); + button.closest("li")?.classList.toggle("active", selected); + }); + } + + function teardownEditRequestCurlExample(opblock) { + opblock.querySelector(":scope .dstack-swagger-edit-curl-example")?.remove(); + } + + function getRequestJsonSchema(operation, opblock) { + const mediaType = getRequestContentType(opblock, operation); + return operation?.requestBody?.content?.[mediaType]?.schema || + getContentSchema(operation?.requestBody?.content); + } + + function buildJsonSchemaDocument(schema, spec) { + const definitions = {}; + const seenDefinitions = new Set(); + const addDefinition = (name) => { + if (!name || seenDefinitions.has(name)) { + return Boolean(name); + } + const componentSchema = spec.components?.schemas?.[name]; + if (!componentSchema) { + return false; + } + seenDefinitions.add(name); + definitions[name] = transformOpenApiSchemaToJsonSchema( + componentSchema, + addDefinition, + spec + ); + return true; + }; + + const rootSchema = resolveSchema(schema, spec) || schema; + const schemaDocument = { + $schema: "https://json-schema.org/draft/2020-12/schema", + ...transformOpenApiSchemaToJsonSchema(rootSchema, addDefinition, spec), + }; + if (Object.keys(definitions).length > 0) { + schemaDocument.$defs = orderJsonSchemaDefinitions(definitions); + } + return schemaDocument; + } + + function orderJsonSchemaDefinitions(definitions) { + return Object.fromEntries( + Object.entries(definitions).map(([name, definition]) => [ + name, + orderJsonSchemaKeys(definition), + ]) + ); + } + + function orderJsonSchemaKeys(value) { + if (Array.isArray(value)) { + return value.map(orderJsonSchemaKeys); + } + if (!value || typeof value !== "object") { + return value; + } + + const keyOrder = [ + "$schema", + "$id", + "$ref", + "type", + "const", + "enum", + "required", + "properties", + "items", + "additionalProperties", + "oneOf", + "anyOf", + "allOf", + "not", + "format", + "minimum", + "maximum", + "exclusiveMinimum", + "exclusiveMaximum", + "minLength", + "maxLength", + "pattern", + "minItems", + "maxItems", + "uniqueItems", + "description", + "default", + "$defs", + ]; + const ordered = {}; + keyOrder.forEach((key) => { + if (Object.prototype.hasOwnProperty.call(value, key)) { + ordered[key] = orderJsonSchemaKeys(value[key]); + } + }); + Object.keys(value).forEach((key) => { + if (!Object.prototype.hasOwnProperty.call(ordered, key)) { + ordered[key] = orderJsonSchemaKeys(value[key]); + } + }); + return ordered; + } + + function transformOpenApiSchemaToJsonSchema(schema, addDefinition, spec) { + if (!schema || typeof schema !== "object") { + return schema; + } + if (Array.isArray(schema)) { + return schema.map((item) => + transformOpenApiSchemaToJsonSchema(item, addDefinition, spec) + ); + } + + const nullable = schema.nullable === true; + const transformed = {}; + Object.entries(schema).forEach(([key, value]) => { + if (key === "nullable" || isOpenApiOnlySchemaKeyword(key)) { + return; + } + + if (key === "$ref" && typeof value === "string") { + Object.assign(transformed, transformSchemaRef(value, addDefinition, spec)); + return; + } + + if (key === "exclusiveMinimum") { + transformExclusiveLimit(transformed, "minimum", "exclusiveMinimum", value, schema); + return; + } + if (key === "exclusiveMaximum") { + transformExclusiveLimit(transformed, "maximum", "exclusiveMaximum", value, schema); + return; + } + + transformed[key] = transformOpenApiSchemaToJsonSchema(value, addDefinition, spec); + }); + if (schema.exclusiveMinimum === true && schema.minimum !== undefined) { + delete transformed.minimum; + } + if (schema.exclusiveMaximum === true && schema.maximum !== undefined) { + delete transformed.maximum; + } + + return nullable ? addNullableType(transformed) : transformed; + } + + function isOpenApiOnlySchemaKeyword(key) { + return ["discriminator", "example", "externalDocs", "xml"].includes(key); + } + + function transformSchemaRef(ref, addDefinition, spec) { + const schemaName = getComponentSchemaRefName(ref); + if (!schemaName) { + return { $ref: ref }; + } + if (addDefinition(schemaName)) { + return { $ref: `#/$defs/${schemaName}` }; + } + + const componentSchema = spec.components?.schemas?.[schemaName]; + if (!componentSchema) { + return { $ref: ref }; + } + return { $ref: ref }; + } + + function getComponentSchemaRefName(ref) { + const prefix = "#/components/schemas/"; + return ref.startsWith(prefix) ? ref.slice(prefix.length) : null; + } + + function transformExclusiveLimit(target, limitKey, exclusiveKey, value, source) { + if (typeof value !== "boolean") { + target[exclusiveKey] = value; + return; + } + if (value && source[limitKey] !== undefined) { + target[exclusiveKey] = source[limitKey]; + delete target[limitKey]; + } + } + + function addNullableType(schema) { + if (typeof schema.type === "string") { + return { + ...schema, + type: schema.type === "null" ? "null" : [schema.type, "null"], + }; + } + if (Array.isArray(schema.type)) { + return { + ...schema, + type: schema.type.includes("null") ? schema.type : [...schema.type, "null"], + }; + } + return { + anyOf: [ + schema, + { + type: "null", + }, + ], + }; + } + + function getRequestExampleBody(modelExample, operation, opblock) { + const cached = modelExample.dataset.dstackSwaggerCurlBody; + const bodyFromDom = getRequestExampleBodyFromDom(modelExample); + if (bodyFromDom) { + modelExample.dataset.dstackSwaggerCurlBody = bodyFromDom; + return bodyFromDom; + } + if (cached) { + return cached; + } + + const mediaType = getRequestContentType(opblock, operation); + const bodyFromSpec = getRequestExampleBodyFromSpec(operation, mediaType); + if (bodyFromSpec) { + modelExample.dataset.dstackSwaggerCurlBody = bodyFromSpec; + } + return bodyFromSpec; + } + + function getRequestExampleBodyFromDom(modelExample) { + const candidates = [ + ...modelExample.querySelectorAll("textarea:not(.curl), .body-param__text"), + ...modelExample.querySelectorAll( + "[role='tabpanel'] pre, [role='tabpanel'] code, pre, code" + ), + ]; + for (const candidate of candidates) { + const text = (candidate.value || candidate.textContent || "").trim(); + if (looksLikeRequestBody(text)) { + return text; + } + } + return ""; + } + + function looksLikeRequestBody(text) { + if (!text || /^(schema|object)$/i.test(text)) { + return false; + } + if (/^\s*[{[]/.test(text)) { + return true; + } + return text.length > 0 && text.length < 10000; + } + + function getRequestExampleBodyFromSpec(operation, mediaType) { + const media = operation.requestBody?.content?.[mediaType]; + if (!media) { + return ""; + } + if (media.example !== undefined) { + return stringifyCurlBody(media.example); + } + + const firstExample = Object.values(media.examples || {})[0]; + if (firstExample?.value !== undefined) { + return stringifyCurlBody(firstExample.value); + } + return ""; + } + + function stringifyCurlBody(value) { + return typeof value === "string" ? value : JSON.stringify(value, null, 2); + } + + function buildCurlCommand(opblock, operation, spec, body) { + const method = (opblock.querySelector(".opblock-summary-method")?.textContent || "GET") + .trim() + .toUpperCase(); + const parameters = getOperationParameters(opblock, operation, spec); + const url = getCurlUrl(opblock, parameters); + const mediaType = getRequestContentType(opblock, operation); + const lines = [`curl -X ${method} '${escapeShellSingleQuoted(url)}'`]; + + if (hasSecurity(operation, spec)) { + lines.push("-H 'Authorization: Bearer {user token}'"); + } + addCurlParameterHeaders(lines, parameters); + lines.push("-H 'Accept: application/json'"); + if (mediaType) { + lines.push(`-H 'Content-Type: ${escapeShellSingleQuoted(mediaType)}'`); + } + if (body && method !== "GET" && method !== "HEAD") { + lines.push(formatCurlBodyArgument(body)); + } + return lines.join(` \\\n${CURL_CONTINUATION_INDENT}`); + } + + function formatCurlBodyArgument(body) { + const value = indentCurlBody(escapeShellSingleQuoted(String(body).trim())); + return `-d '${value}'`; + } + + function indentCurlBody(value) { + return value.replace(/\n/g, `\n${CURL_CONTINUATION_INDENT}`); + } + + function getCurlUrl(opblock, parameters) { + const url = getOperationUrl(opblock); + const query = parameters + .filter((parameter) => parameter.in === "query" && parameter.required) + .map((parameter) => { + return `${encodeURIComponent(parameter.name)}=${getParameterPlaceholder(parameter.name)}`; + }) + .join("&"); + if (!query) { + return url; + } + return `${url}${url.includes("?") ? "&" : "?"}${query}`; + } + + function addCurlParameterHeaders(lines, parameters) { + parameters + .filter((parameter) => parameter.in === "header" && parameter.required) + .forEach((parameter) => { + lines.push( + `-H '${escapeShellSingleQuoted(parameter.name)}: ${escapeShellSingleQuoted( + getParameterPlaceholder(parameter.name) + )}'` + ); + }); + + const cookies = parameters + .filter((parameter) => parameter.in === "cookie" && parameter.required) + .map((parameter) => { + return `${parameter.name}=${getParameterPlaceholder(parameter.name)}`; + }); + if (cookies.length > 0) { + lines.push(`-H 'Cookie: ${escapeShellSingleQuoted(cookies.join("; "))}'`); + } + } + + function getOperationParameters(opblock, operation, spec) { + const path = opblock.querySelector(".opblock-summary-path")?.dataset.path; + const pathItem = path ? spec.paths?.[path] : null; + const parameters = new Map(); + [...(pathItem?.parameters || []), ...(operation.parameters || [])].forEach((parameter) => { + const resolvedParameter = resolveParameter(parameter, spec); + if (!resolvedParameter?.name || !resolvedParameter?.in) { + return; + } + parameters.set(`${resolvedParameter.in}:${resolvedParameter.name}`, resolvedParameter); + }); + return [...parameters.values()]; + } + + function resolveParameter(parameter, spec) { + if (typeof parameter?.$ref !== "string") { + return parameter; + } + const name = parameter.$ref.split("/").pop(); + return spec.components?.parameters?.[name] || parameter; + } + + function getParameterPlaceholder(name) { + return `{${name}}`; + } + + function getRequestContentType(opblock, operation) { + return ( + opblock.querySelector(".dstack-swagger-content-type-proxy select")?.value || + opblock.querySelector(".opblock-section-request-body .content-type-wrapper select")?.value || + Object.keys(operation?.requestBody?.content || {})[0] || + "" + ); + } + + function hasSecurity(operation, spec) { + if (Array.isArray(operation.security)) { + return operation.security.length > 0; + } + return Array.isArray(spec.security) && spec.security.length > 0; + } + + function escapeShellSingleQuoted(value) { + return String(value).replace(/'/g, "'\\''"); + } + + function getOperationAnchors(root) { + const pageRoot = root.closest(".md-content__inner, .md-typeset, article") || document; + const anchors = new Map(); + pageRoot.querySelectorAll(".dstack-swagger-operation-anchor").forEach((anchor) => { + const key = getOperationKey(anchor.dataset.openapiMethod, anchor.dataset.openapiPath); + if (key) { + anchors.set(key, anchor); + } + }); + return anchors; + } + + function getOperationKeyForOpblock(opblock) { + return getOperationKey( + opblock.querySelector(".opblock-summary-method")?.textContent, + opblock.querySelector(".opblock-summary-path")?.dataset.path + ); + } + + function getOperationKey(method, path) { + const normalizedMethod = (method || "").trim().toLowerCase(); + const normalizedPath = (path || "").trim(); + return normalizedMethod && normalizedPath ? `${normalizedMethod} ${normalizedPath}` : null; + } + + function setupOperationTocScrolling(root) { + if (document.documentElement.dataset.dstackSwaggerTocScrolling === "true") { + return; + } + document.documentElement.dataset.dstackSwaggerTocScrolling = "true"; + + document.addEventListener("click", (event) => { + const target = event.target instanceof Element ? event.target : event.target.parentElement; + const link = target?.closest("a[href*='#']"); + const anchor = getOperationTitleForLink(link); + if (!anchor) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + window.history.pushState(null, "", `#${anchor.id}`); + scrollToOperationTitle(anchor, "smooth"); + }, true); + + scrollToCurrentOperationHash(root); + } + + function scrollToCurrentOperationHash(root) { + if (root.dataset.dstackSwaggerHashScrolled === "true" || !window.location.hash) { + return; + } + const anchor = document.getElementById(decodeHashId(window.location.hash.slice(1))); + if (!anchor?.classList.contains("dstack-swagger-operation-title") || !root.contains(anchor)) { + return; + } + + root.dataset.dstackSwaggerHashScrolled = "true"; + window.requestAnimationFrame(() => scrollToOperationTitle(anchor)); + } + + function getOperationTitleForLink(link) { + if (!link) { + return null; + } + + const url = new URL(link.getAttribute("href"), window.location.href); + if ( + url.origin !== window.location.origin || + url.pathname !== window.location.pathname || + url.search !== window.location.search || + !url.hash + ) { + return null; + } + + const anchor = document.getElementById(decodeHashId(url.hash.slice(1))); + return anchor?.classList.contains("dstack-swagger-operation-title") ? anchor : null; + } + + function scrollToOperationTitle(anchor, behavior = "auto") { + const style = window.getComputedStyle(anchor); + const scrollMargin = Number.parseFloat(style.scrollMarginTop) || 0; + const top = anchor.getBoundingClientRect().top + window.scrollY - scrollMargin; + window.scrollTo({ top, behavior }); + } + + function decodeHashId(hashId) { + try { + return decodeURIComponent(hashId); + } catch { + return hashId; + } + } + + function stripSchemaTitles(spec) { + const seen = new WeakSet(); + const strip = (schema) => { + if (!schema || typeof schema !== "object" || seen.has(schema)) { + return; + } + seen.add(schema); + + delete schema.title; + + Object.values(schema.properties || {}).forEach(strip); + ["items", "additionalProperties", "not"].forEach((key) => { + if (schema[key] && typeof schema[key] === "object") { + strip(schema[key]); + } + }); + ["allOf", "anyOf", "oneOf"].forEach((key) => { + if (Array.isArray(schema[key])) { + schema[key].forEach(strip); + } + }); + }; + + Object.values(spec.components?.schemas || {}).forEach(strip); + Object.values(spec.paths || {}).forEach((pathItem) => { + Object.values(pathItem || {}).forEach((operation) => { + (operation.parameters || []).forEach((parameter) => strip(parameter.schema)); + stripContentSchemas(operation.requestBody?.content, strip); + Object.values(operation.responses || {}).forEach((response) => { + stripContentSchemas(response.content, strip); + }); + }); + }); + } + + function stripContentSchemas(content, strip) { + Object.values(content || {}).forEach((mediaType) => { + strip(mediaType?.schema); + }); + } + + function setupSchemaNameBadges(root, spec) { + let scheduled = false; + const update = () => { + scheduled = false; + updateSchemaNameBadges(root, spec); + }; + const scheduleUpdate = () => { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(update); + }; + + const observer = new MutationObserver(scheduleUpdate); + observer.observe(root, { + childList: true, + subtree: true, + }); + scheduleUpdate(); + } + + function setupModelPropertyLabels(root, spec) { + let scheduled = false; + const update = () => { + scheduled = false; + updateModelPropertyLabels(root, spec); + }; + const scheduleUpdate = () => { + if (scheduled) { + return; + } + scheduled = true; + window.requestAnimationFrame(update); + }; + + const observer = new MutationObserver(scheduleUpdate); + observer.observe(root, { + childList: true, + subtree: true, + }); + scheduleUpdate(); + } + + function updateSchemaNameBadges(root, spec) { + root.querySelectorAll(".opblock").forEach((opblock) => { + const operation = getOperationForOpblock(spec, opblock); + if (!operation) { + return; + } + + const schemas = getOperationSchemas(operation); + opblock.querySelectorAll(".model-example").forEach((modelExample, index) => { + const schema = schemas[index]; + if (!schema) { + return; + } + const rootArticle = modelExample.querySelector(".model-box > .json-schema-2020-12"); + if (rootArticle) { + updateSchemaArticle(rootArticle, schema, spec); + } + }); + }); + } + + function getOperationForOpblock(spec, opblock) { + const method = (opblock.querySelector(".opblock-summary-method")?.textContent || "") + .trim() + .toLowerCase(); + const path = opblock.querySelector(".opblock-summary-path")?.dataset.path; + if (!method || !path) { + return null; + } + return spec.paths?.[path]?.[method] || null; + } + + function getOperationSchemas(operation) { + const schemas = []; + const requestSchema = getContentSchema(operation.requestBody?.content); + if (requestSchema && !isEmptySchema(requestSchema)) { + schemas.push(requestSchema); + } + + Object.values(operation.responses || {}).forEach((response) => { + const responseSchema = getContentSchema(response.content); + if (responseSchema && !isEmptySchema(responseSchema)) { + schemas.push(responseSchema); + } + }); + return schemas; + } + + function getContentSchema(content) { + for (const mediaType of Object.values(content || {})) { + if (mediaType?.schema) { + return mediaType.schema; + } + } + return null; + } + + function isEmptySchema(schema) { + return Object.keys(schema || {}).length === 0; + } + + function updateModelPropertyLabels(root, spec) { + root.querySelectorAll(".opblock").forEach((opblock) => { + const operation = getOperationForOpblock(spec, opblock); + if (!operation) { + return; + } + + const schemas = getOperationSchemas(operation); + opblock.querySelectorAll(".model-example").forEach((modelExample, index) => { + const schema = schemas[index]; + if (!schema) { + return; + } + updateModelExample(modelExample, schema, spec); + }); + }); + + root.querySelectorAll(".model-example").forEach((modelExample) => { + modelExample + .querySelectorAll('.model-box-control[aria-expanded="true"]') + .forEach((button) => { + const schemaName = getModelSchemaName(button, spec); + const schema = schemaName ? spec.components?.schemas?.[schemaName] : null; + if (schema) { + updateModelWrapperPropertyLabels(button.parentElement, schema, spec); + } + }); + updateModelTitleLabels(modelExample, spec); + updateBareObjectControlTitles(modelExample); + }); + } + + function updateModelExample(modelExample, schema, spec) { + const modelBox = modelExample.querySelector(":scope .model-box"); + if (modelBox) { + updateModelControlTitle(modelBox.querySelector(":scope > .model-box-control"), schema, spec); + updateModelWrapperPropertyLabels(modelBox, schema, spec); + } + updateModelTitleLabels(modelExample, spec); + updateBareObjectControlTitles(modelExample); + } + + function updateModelWrapperPropertyLabels(wrapper, schema, spec) { + if (!wrapper) { + return; + } + const properties = getSchemaProperties(schema, spec); + if (!properties) { + return; + } + + wrapper + .querySelectorAll(":scope > .inner-object > table.model > tbody > tr.property-row") + .forEach((row) => { + const propertyName = getPropertyRowName(row); + const propertySchema = properties[propertyName]; + const label = getSimpleSchemaLabel(propertySchema, spec); + if (label) { + setPropertyRowLabel(row, label); + return; + } + + const valueCell = row.cells?.[1]; + updateModelControlTitle( + valueCell?.querySelector(".model-box-control"), + propertySchema, + spec + ); + updateModelWrapperPropertyLabels( + valueCell?.querySelector(".model-box"), + propertySchema, + spec + ); + }); + } + + function getModelTitle(button) { + return ( + button.querySelector(".model-title__text")?.textContent || + button.querySelector(".model-title")?.textContent || + "" + ).trim(); + } + + function getModelSchemaName(button, spec) { + if (!button) { + return null; + } + if (button.dataset.dstackSwaggerSchemaName) { + return button.dataset.dstackSwaggerSchemaName; + } + const title = getModelTitle(button); + const schemaName = getSchemaNameFromTitle(title, spec); + if (schemaName) { + button.dataset.dstackSwaggerSchemaName = schemaName; + } + return schemaName; + } + + function getSchemaNameFromTitle(title, spec) { + const normalized = (title || "").trim(); + if (!normalized) { + return null; + } + if (spec.components?.schemas?.[normalized]) { + return normalized; + } + + return null; + } + + function getPropertyRowName(row) { + const name = row.cells?.[0]?.textContent || ""; + return name.trim().replace(/\s*\*$/, ""); + } + + function setPropertyRowLabel(row, label) { + const valueCell = row.cells?.[1]; + if (!valueCell || valueCell.dataset.dstackSwaggerLabel === label) { + return; + } + valueCell.dataset.dstackSwaggerLabel = label; + valueCell.replaceChildren(); + + const labelNode = document.createElement("span"); + labelNode.className = "dstack-swagger-model-label"; + labelNode.textContent = label; + valueCell.appendChild(labelNode); + } + + function updateModelTitleLabels(container, spec) { + container.querySelectorAll(".model-box-control").forEach((button) => { + getModelSchemaName(button, spec); + const title = getModelTitle(button); + const schema = getSchemaForModelTitle(title, spec); + if (schema) { + updateModelControlTitle(button, schema, spec); + } + }); + } + + function updateBareObjectControlTitles(container) { + container.querySelectorAll(".model-box-control").forEach((button) => { + if ( + button.querySelector(".model-title__text") || + button.querySelector(":scope > .dstack-swagger-model-inline-title") || + !isBareObjectControl(button) + ) { + return; + } + insertModelControlTitle(button, "object"); + }); + } + + function isBareObjectControl(button) { + return Array.from(button.children).some((child) => { + const text = (child.textContent || "").trim().replace(/\s+/g, ""); + return text === "{...}" || text.startsWith("{..."); + }); + } + + function getSchemaForModelTitle(title, spec) { + const normalized = (title || "").trim(); + const schemaName = getSchemaNameFromTitle(normalized, spec); + if (schemaName) { + return spec.components?.schemas?.[schemaName] || null; + } + + const arrayMatch = normalized.match(/^array<(.+)>$/i); + if (arrayMatch) { + const itemName = arrayMatch[1].trim(); + if (spec.components?.schemas?.[itemName]) { + return { + type: "array", + items: { + $ref: `#/components/schemas/${itemName}`, + }, + }; + } + } + + return null; + } + + function updateModelControlTitle(button, schema, spec) { + const title = button?.querySelector(".model-title__text"); + const label = getModelTitleLabel(schema, spec); + if (!button || !label) { + return; + } + if (!title) { + insertModelControlTitle(button, label); + return; + } + if (title.dataset.dstackSwaggerTitle === label) { + return; + } + title.dataset.dstackSwaggerTitle = label; + title.textContent = label; + } + + function insertModelControlTitle(button, label) { + if (button.dataset.dstackSwaggerTitle === label) { + return; + } + + const existing = button.querySelector(":scope > .dstack-swagger-model-inline-title"); + if (existing) { + existing.textContent = label; + button.dataset.dstackSwaggerTitle = label; + return; + } + + const titleNode = document.createElement("span"); + titleNode.className = "dstack-swagger-model-inline-title"; + titleNode.textContent = label; + button.insertBefore(titleNode, getModelPlaceholderNode(button)); + button.dataset.dstackSwaggerTitle = label; + } + + function getModelPlaceholderNode(button) { + const directChildren = Array.from(button.children); + return ( + directChildren.find((child) => { + const text = (child.textContent || "").trim(); + return text === "{...}" || text.startsWith("{") || text === "[...]"; + }) || + directChildren.find((child) => child.classList.contains("model-toggle")) || + button.firstChild + ); + } + + function getSimpleSchemaLabel(schema, spec) { + if (!schema) { + return null; + } + + const refSchema = resolveSchema(schema, spec); + if (refSchema && refSchema !== schema) { + const label = getSimpleSchemaLabel(refSchema, spec); + if (label) { + return label; + } + } + + if (Array.isArray(schema.allOf) && schema.allOf.length === 1) { + return getSimpleSchemaLabel(schema.allOf[0], spec); + } + + const union = schema.anyOf || schema.oneOf; + if (Array.isArray(union) && union.length > 0) { + const labels = union.map((item) => getSimpleSchemaLabel(item, spec)); + if (labels.every(Boolean)) { + return labels.join(" | "); + } + return null; + } + + if (Array.isArray(schema.enum)) { + return formatEnumValues(schema.enum); + } + + if (schema.const !== undefined) { + return formatEnumValue(schema.const); + } + + if (schema.type === "array") { + const itemLabel = getSimpleSchemaLabel(schema.items || {}, spec); + return itemLabel ? `array<${itemLabel}>` : null; + } + + if (schema.type === "object") { + const additionalProperties = schema.additionalProperties; + if (additionalProperties && typeof additionalProperties === "object") { + const valueLabel = getSimpleSchemaLabel(additionalProperties, spec); + return valueLabel ? `object` : null; + } + return null; + } + + if (["string", "integer", "number", "boolean", "null"].includes(schema.type)) { + return schema.type; + } + + return null; + } + + function getModelTitleLabel(schema, spec) { + if (!schema) { + return null; + } + + const refSchema = resolveSchema(schema, spec); + if (refSchema && refSchema !== schema) { + return getModelTitleLabel(refSchema, spec); + } + + if (Array.isArray(schema.allOf) && schema.allOf.length === 1) { + return getModelTitleLabel(schema.allOf[0], spec); + } + + const union = schema.anyOf || schema.oneOf; + if (Array.isArray(union) && union.length > 0) { + const labels = uniqueLabels(union.map((item) => getModelTitleLabel(item, spec))); + return labels.length > 0 ? labels.join(" | ") : "oneOf"; + } + + const simpleLabel = getSimpleSchemaLabel(schema, spec); + if (simpleLabel) { + return simpleLabel; + } + + if (schema.type === "array") { + const itemLabel = getModelTitleLabel(schema.items || {}, spec); + return itemLabel ? `array<${itemLabel}>` : "array"; + } + + if (schema.type === "object" || schema.properties) { + return "object"; + } + + return null; + } + + function uniqueLabels(labels) { + const seen = new Set(); + return labels.filter((label) => { + if (!label || seen.has(label)) { + return false; + } + seen.add(label); + return true; + }); + } + + function formatEnumValue(value) { + if (value === null) { + return "null"; + } + if (value === undefined) { + return "undefined"; + } + return String(value); + } + + function formatEnumValues(values) { + return values.map(formatEnumValue).join(" | "); + } + + function updateSchemaArticle(article, schema, spec) { + const badge = article.querySelector( + ":scope > .json-schema-2020-12-head .json-schema-2020-12__attribute--primary" + ); + const label = getModelTitleLabel(schema, spec); + if (badge && label) { + const current = (badge.textContent || "").trim(); + const schemaName = getRefName(schema); + if (schemaName && current === schemaName) { + badge.textContent = label; + } + } + + const resolvedSchema = resolveSchema(schema, spec) || schema; + updateItemArticle(article, resolvedSchema, spec); + updatePropertyArticles(article, resolvedSchema, spec); + } + + function updateItemArticle(article, schema, spec) { + const itemArticle = article.querySelector( + ":scope > .json-schema-2020-12-body .json-schema-2020-12-keyword--items > article.json-schema-2020-12" + ); + if (itemArticle && schema?.items) { + updateSchemaArticle(itemArticle, schema.items, spec); + } + } + + function getSchemaProperties(schema, spec) { + const resolvedSchema = resolveSchema(schema, spec) || schema; + if (resolvedSchema?.properties) { + return resolvedSchema.properties; + } + + if (Array.isArray(resolvedSchema?.allOf)) { + return resolvedSchema.allOf.reduce((properties, item) => { + return { + ...properties, + ...(getSchemaProperties(item, spec) || {}), + }; + }, {}); + } + + return null; + } + + function updatePropertyArticles(article, schema, spec) { + const properties = resolveSchema(schema, spec)?.properties || schema?.properties; + if (!properties) { + return; + } + + article + .querySelectorAll( + ":scope > .json-schema-2020-12-body .json-schema-2020-12-keyword--properties > ul > li > article.json-schema-2020-12" + ) + .forEach((propertyArticle) => { + const propertyName = getDirectTitle(propertyArticle); + const propertySchema = properties[propertyName]; + if (propertySchema) { + updateSchemaArticle(propertyArticle, propertySchema, spec); + } + }); + } + + function getDirectTitle(article) { + return ( + article + .querySelector(":scope > .json-schema-2020-12-head .json-schema-2020-12__title") + ?.textContent || "" + ).trim(); + } + + function resolveSchema(schema, spec) { + const ref = getRef(schema); + if (!ref) { + return schema; + } + const name = ref.split("/").pop(); + return spec.components?.schemas?.[name] || schema; + } + + function getRefName(schema) { + const ref = getRef(schema); + return ref ? ref.split("/").pop() : null; + } + + function getRef(schema) { + if (typeof schema?.$ref === "string") { + return schema.$ref; + } + if (Array.isArray(schema?.allOf) && schema.allOf.length === 1) { + return getRef(schema.allOf[0]); + } + return null; + } + + if (document.readyState === "loading") { + window.addEventListener("DOMContentLoaded", initSwaggerReferences); + } else { + initSwaggerReferences(); + } +})(); diff --git a/mkdocs/assets/javascripts/termynal.js b/mkdocs/assets/javascripts/termynal.js index 9a634c6a6d..70f99eabc9 100644 --- a/mkdocs/assets/javascripts/termynal.js +++ b/mkdocs/assets/javascripts/termynal.js @@ -30,12 +30,21 @@ class Termynal { constructor(container = '#termynal', options = {}) { this.container = (typeof container === 'string') ? document.querySelector(container) : container; this.pfx = `data-${options.prefix || 'ty'}`; - this.originalStartDelay = this.startDelay = options.startDelay - || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 300; - this.originalTypeDelay = this.typeDelay = options.typeDelay - || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 60; - this.originalLineDelay = this.lineDelay = options.lineDelay - || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500; + this.originalStartDelay = this.startDelay = this.getOptionNumber( + options.startDelay, + `${this.pfx}-startDelay`, + 300 + ); + this.originalTypeDelay = this.typeDelay = this.getOptionNumber( + options.typeDelay, + `${this.pfx}-typeDelay`, + 60 + ); + this.originalLineDelay = this.lineDelay = this.getOptionNumber( + options.lineDelay, + `${this.pfx}-lineDelay`, + 1500 + ); this.progressLength = options.progressLength || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40; this.progressChar = options.progressChar @@ -45,22 +54,30 @@ class Termynal { this.cursor = options.cursor || this.container.getAttribute(`${this.pfx}-cursor`) || '▋'; this.lineData = this.lineDataToElements(options.lineData || []); + this.lineContainer = null; this.loadLines() if (!options.noInit) this.init() } + getOptionNumber(value, attr, fallback) { + if (value !== undefined && value !== null) { + return value; + } + const attrValue = parseFloat(this.container.getAttribute(attr)); + return Number.isNaN(attrValue) ? fallback : attrValue; + } + loadLines() { // Load all the lines and create the container so that the size is fixed // Otherwise it would be changing and the user viewport would be constantly // moving as she/he scrolls - const finish = this.generateFinish() - finish.style.visibility = 'hidden' - this.container.appendChild(finish) // Appends dynamically loaded lines to existing line elements. this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData); + const lineParent = this.container.classList.contains('dstack-termy-scrollable') ? + this.getLineContainer() : this.container; for (let line of this.lines) { line.style.visibility = 'hidden' - this.container.appendChild(line) + lineParent.appendChild(line) } const restart = this.generateRestart() restart.style.visibility = 'hidden' @@ -79,11 +96,15 @@ class Termynal { const containerStyle = getComputedStyle(this.container); this.container.style.width = containerStyle.width !== '0px' ? containerStyle.width : undefined; - this.container.style.minHeight = containerStyle.height !== '0px' ? - containerStyle.height : undefined; + if (!this.container.classList.contains('dstack-termy-scrollable') && containerStyle.height !== '0px') { + this.container.style.minHeight = containerStyle.height; + } else { + this.container.style.minHeight = ''; + } this.container.setAttribute('data-termynal', ''); this.container.innerHTML = ''; + this.lineContainer = null; for (let line of this.lines) { line.style.visibility = 'visible' } @@ -94,7 +115,6 @@ class Termynal { * Start the animation and rener the lines depending on their data attributes. */ async start() { - this.addFinish() await this._wait(this.startDelay); for (let line of this.lines) { @@ -113,7 +133,7 @@ class Termynal { } else { - this.container.appendChild(line); + this.appendLine(line); await this._wait(delay); } @@ -135,7 +155,6 @@ class Termynal { this.lines[lastInputIdx].setAttribute(`${this.pfx}-cursor`, this.cursor); } this.addRestart() - this.finishElement.style.visibility = 'hidden' this.lineDelay = this.originalLineDelay this.typeDelay = this.originalTypeDelay this.startDelay = this.originalStartDelay @@ -148,37 +167,17 @@ class Termynal { this.container.innerHTML = '' this.init() } - restart.href = '#' + restart.href = 'javascript:void(0)' restart.setAttribute('data-terminal-control', '') restart.innerHTML = "restart ↻" return restart } - generateFinish() { - const finish = document.createElement('a') - finish.onclick = (e) => { - e.preventDefault() - this.lineDelay = 0 - this.typeDelay = 0 - this.startDelay = 0 - } - finish.href = '#' - finish.setAttribute('data-terminal-control', '') - finish.innerHTML = "fast →" - this.finishElement = finish - return finish - } - addRestart() { const restart = this.generateRestart() this.container.appendChild(restart) } - addFinish() { - const finish = this.generateFinish() - this.container.appendChild(finish) - } - /** * Animate a typed line. * @param {Node} line - The line element to render. @@ -186,10 +185,14 @@ class Termynal { async type(line) { const chars = [...line.textContent]; line.textContent = ''; - this.container.appendChild(line); + this.appendLine(line); + const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; + if (delay <= 0) { + line.textContent = chars.join(''); + return; + } for (let char of chars) { - const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; await this._wait(delay); line.textContent += char; } @@ -207,10 +210,14 @@ class Termynal { const chars = progressChar.repeat(progressLength); const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`) || this.progressPercent; - const typeDelay = line.getAttribute(`${this.pfx}-typeDelay`) - || this.typeDelay; + const typeDelay = line.getAttribute(`${this.pfx}-typeDelay`) + || this.typeDelay; line.textContent = ''; - this.container.appendChild(line); + this.appendLine(line); + if (typeDelay <= 0) { + line.textContent = this.getFullProgressText(progressLength, progressChar, progressPercent); + return; + } for (let i = 1; i < chars.length + 1; i++) { await this._wait(typeDelay); @@ -222,11 +229,35 @@ class Termynal { } } + appendLine(line) { + this.getLineContainer().appendChild(line); + } + + getLineContainer() { + if (!this.container.classList.contains('dstack-termy-scrollable')) { + return this.container; + } + if (!this.lineContainer) { + this.lineContainer = document.createElement('div'); + this.lineContainer.setAttribute('data-termynal-body', ''); + this.container.appendChild(this.lineContainer); + } + return this.lineContainer; + } + + getFullProgressText(progressLength, progressChar, progressPercent) { + const visibleLength = Math.ceil(progressLength * progressPercent / 100); + return `${progressChar.repeat(visibleLength)} ${progressPercent}%`; + } + /** * Helper function for animation delays, called with `await`. * @param {number} time - Timeout, in ms. */ _wait(time) { + if (time <= 0) { + return Promise.resolve(); + } return new Promise(resolve => setTimeout(resolve, time)); } diff --git a/mkdocs/assets/stylesheets/extra.css b/mkdocs/assets/stylesheets/extra.css index e344baf9d0..937369742a 100644 --- a/mkdocs/assets/stylesheets/extra.css +++ b/mkdocs/assets/stylesheets/extra.css @@ -1,3 +1,8 @@ +html { + --dstack-code-font-size: 14px; + --dstack-code-line-height: 1.075rem; +} + @media screen and (min-width: 76.1875em) { .md-header { backdrop-filter: blur(5px); @@ -24,15 +29,6 @@ } } -[dir=ltr] .md-typeset :is(.admonition,details) pre, -[dir=ltr] .md-typeset :is(.admonition,details) :is(.admonition,details, .termy) { - margin-left: 32px; -} - -[dir=ltr] .md-typeset :is(.md-typeset__scrollwrap,p,h4,h3,.tabbed-set,ul):not(.admonition-title) :is(.admonition,details, .termy) { - margin-left: 0; -} - @media screen and (max-width: 76.1875em) { .md-header { background-color: rgb(255, 255, 255); @@ -190,16 +186,22 @@ /*background: -webkit-linear-gradient(45deg, rgba(0, 42, 255, 0.1), rgb(0 114 255 / 1%), rgba(0, 42, 255, 0.05));*/ } -.md-typeset iframe { - border-radius: 0; +[dir=ltr] + .md-typeset + :is(.admonition, details) + > :is(.md-typeset__scrollwrap, p, h4, h3, .tabbed-set, ul, ol):not(.admonition-title, summary) { + padding-left: 32px; } -[dir=ltr] .md-typeset :is(.admonition,details) blockquote { +[dir=ltr] + .md-typeset + :is(.admonition, details) + > :is(pre, blockquote, .highlight, .termy, div[editor-title]) { margin-left: 32px; } -[dir=ltr] .md-typeset :is(.admonition,details):not(blockquote) > :is(.md-typeset__scrollwrap,p,h4,h3,.tabbed-set,ul):not(.admonition-title) { - padding-left: 32px; +.md-typeset iframe { + border-radius: 0; } .md-typeset__scrollwrap { @@ -433,7 +435,7 @@ h4.doc-heading { .md-typeset :not(pre, h1, h2, h3, h4, h5, h6) > code { background-color: rgba(163, 68, 215, 0.05); /*border: 1px solid #dce0e6;*/ - border-radius: 2px; + border-radius: 3px; font-weight: 600; color: var(--md-primary-fg-color); text-align: center; @@ -468,9 +470,10 @@ h4.doc-heading { .md-typeset pre > code { background-color: rgb(21, 22, 29); - padding: 65px 30px 35px 40px; - border-radius: 0; - font-size: 15px; + padding: 55px 15px 35px 30px; + border-radius: 3px; + font-size: var(--dstack-code-font-size); + line-height: var(--dstack-code-line-height); /*border-radius: 0;*/ /*border-top: 1px solid #dce0e6;*/ @@ -498,6 +501,123 @@ h4.doc-heading { box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930; } +.md-typeset :is(.highlight.dstack-scrollable-code, .dstack-editable-code) { + background-color: rgb(21, 22, 29); + box-sizing: border-box; + padding: 55px 15px 35px 30px; + position: relative; +} + +.md-typeset :is(.highlight.dstack-scrollable-code, .dstack-editable-code):before { + background: #d9515d; + border-radius: 50%; + content: ''; + display: inline-block; + height: 12px; + left: 15px; + position: absolute; + top: 15px; + width: 12px; + -webkit-box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930 !important; + box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930 !important; +} + +.md-typeset .highlight.dstack-scrollable-code pre { + background: transparent; + margin: 0; + overflow: visible; +} + +.md-typeset .highlight.dstack-scrollable-code pre > code { + background: transparent; + box-sizing: border-box; + display: block; + line-height: var(--dstack-code-line-height); + max-height: var(--dstack-scrollable-code-max-height, none); + overflow: auto; + padding: 0; + scrollbar-color: #a2a2a2 rgba(255, 255, 255, 0.06); + scrollbar-width: thin; +} + +.md-typeset .dstack-editable-code textarea { + background: transparent; + border: 0; + box-sizing: border-box; + color: var(--md-code-fg-color); + display: block; + font-family: var(--md-code-font-family); + font-size: var(--dstack-code-font-size); + line-height: var(--dstack-code-line-height); + margin: 0; + max-height: var(--dstack-editable-code-max-height, none); + overflow: auto; + padding: 0; + scrollbar-color: #a2a2a2 rgba(255, 255, 255, 0.06); + scrollbar-width: thin; + width: 100%; +} + +.md-typeset .highlight.dstack-scrollable-code pre > code:before { + content: none; +} + +.md-typeset .highlight.dstack-scrollable-code pre > code::-webkit-scrollbar, +.md-typeset .dstack-editable-code textarea::-webkit-scrollbar { + height: 10px; + width: 10px; +} + +.md-typeset .highlight.dstack-scrollable-code pre > code::-webkit-scrollbar-track, +.md-typeset .dstack-editable-code textarea::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.06); +} + +.md-typeset .highlight.dstack-scrollable-code pre > code::-webkit-scrollbar-thumb, +.md-typeset .dstack-editable-code textarea::-webkit-scrollbar-thumb { + background: #a2a2a2; + background-clip: content-box; + border: 2px solid transparent; + border-radius: 8px; +} + +.md-typeset .highlight.dstack-scrollable-code pre > code::-webkit-scrollbar-thumb:hover, +.md-typeset .dstack-editable-code textarea::-webkit-scrollbar-thumb:hover { + background: #e5e5e9; + background-clip: content-box; +} + +.md-typeset .highlight.dstack-scrollable-code > .md-code__nav { + background: transparent; + position: absolute; + right: 15px; + top: 9px; +} + +.md-typeset .highlight.dstack-scrollable-code > .md-code__nav .md-code__button { + color: #a2a2a2; + height: 24px; + padding: 0; + width: 24px; +} + +.md-typeset .highlight.dstack-scrollable-code > .md-code__nav .md-code__button:after { + height: 16px; + margin: 4px; + width: 16px; +} + +.md-typeset .highlight.dstack-scrollable-code:hover > .md-code__nav, +.md-typeset .highlight.dstack-scrollable-code > .md-code__nav:hover { + background: transparent; +} + +.md-typeset .highlight.dstack-scrollable-code:hover > .md-code__nav .md-code__button, +.md-typeset .highlight.dstack-scrollable-code > .md-code__nav:hover .md-code__button { + color: #eee; + opacity: 1; +} + @media screen and (min-width: 60em) { [data-md-color-primary=white] .md-search__form { background-color: transparent; @@ -614,6 +734,10 @@ code .md-code__nav:hover .md-code__button { font-size: 25px; } +.md-typeset :is(h2, h3, h4, h5, h6)[id] { + scroll-margin-top: 3rem; +} + .md-typeset h1, .md-typeset h2 { font-weight: 400; /*letter-spacing: 0;*/ @@ -1073,7 +1197,7 @@ html .md-footer-meta.md-typeset a:is(:focus,:hover) { @media screen and (min-width: 76.1875em) { .md-typeset .tabbed-block > .highlight:first-child > pre > code, .md-typeset .tabbed-block > pre:first-child > code { - border-radius: 0; + border-radius: 3px; } } @@ -1089,12 +1213,12 @@ html .md-footer-meta.md-typeset a:is(:focus,:hover) { } .js .md-typeset .tabbed-labels:before { - height: auto; +height: auto; background: none; z-index: 1; padding: 5px; border-radius: 0; - border: 1px dotted black; + border: 0; bottom: -0.7px; top: -0.7px; left: -0.7px; @@ -1103,7 +1227,7 @@ html .md-footer-meta.md-typeset a:is(:focus,:hover) { } .md-typeset .tabbed-labels > label:after { - content: ""; + content: ""; height: auto; z-index: 1; background: rgba(0,0,0, 0.25); @@ -1112,8 +1236,8 @@ html .md-footer-meta.md-typeset a:is(:focus,:hover) { position: absolute; width: 0.5px; margin-left: -18.5px; - margin-top: 16px; - margin-bottom: 16px; + /* margin-top: 16px; + margin-bottom: 16px; */ } @@ -1797,41 +1921,6 @@ img.border { margin: 0 4px; } - -.swagger-ui .json-schema-2020-12 { - white-space: nowrap !important; - overflow: scroll !important;; -} - -.swagger-ui .json-schema-2020-12:last-of-type { - margin: 0 5px !important; -} - -.swagger-ui .json-schema-2020-12--embedded { - padding-left: 0 !important; -} - -.swagger-ui .json-schema-2020-12-keyword__name { - margin-left: 5px !important; -} - -.swagger-ui .json-schema-2020-12-keyword--description { - margin-left: 0 !important; -} - -.swagger-ui .markdown code, .swagger-ui .renderedMarkdown code { - font-size: 12px !important; -} - -.swagger-ui .tab li:first-of-type { - padding-left: 1rem !important; -} - -.md-typeset .swagger-ui pre > code { - font-size: 12px !important;; - padding: 30px !important; -} - /* External link indicator */ a[href^="http"]:not(:where( /* skip if marked with external-skip */ diff --git a/mkdocs/assets/stylesheets/landing.css b/mkdocs/assets/stylesheets/landing.css index 7801d03455..7659254b24 100644 --- a/mkdocs/assets/stylesheets/landing.css +++ b/mkdocs/assets/stylesheets/landing.css @@ -1078,7 +1078,8 @@ margin: 0; } -.code-carousel .code-carousel__slide[editor-title] pre > code { +.code-carousel .code-carousel__slide[editor-title] pre > code, +.code-carousel .code-carousel__slide[data-termynal] { font-size: 14px; padding-top: 70px; } diff --git a/mkdocs/assets/stylesheets/swagger.css b/mkdocs/assets/stylesheets/swagger.css new file mode 100644 index 0000000000..c9aef08747 --- /dev/null +++ b/mkdocs/assets/stylesheets/swagger.css @@ -0,0 +1,1773 @@ +.dstack-swagger-ui { + --dstack-swagger-border-color: rgba(0, 0, 0, 0.87); + --dstack-swagger-code-bg-color: rgb(21, 22, 29); + --dstack-swagger-hairline-border: 0.5px solid black; + --dstack-swagger-anchor-offset: 4rem; + --dstack-swagger-operation-anchor-offset: 3rem; + --dstack-swagger-control-font-size: 0.7rem; + --dstack-swagger-control-height: 1.84rem; + --dstack-swagger-muted-bg-color: rgba(0, 0, 0, 0.005); + --dstack-swagger-primary-border: 1.5px solid var(--dstack-swagger-border-color); + --dstack-swagger-quote-bg: -webkit-linear-gradient(45deg, rgba(0, 42, 255, 0.1), rgb(0 114 255 / 1%), rgba(0, 42, 255, 0.05)); + --dstack-swagger-action-radius: 4px; + --dstack-swagger-curl-max-height: min(520px, 70vh); + --dstack-swagger-schema-max-height: min(520px, 70vh); + --dstack-swagger-schema-code-max-height: min(420px, calc(70vh - 100px)); + --dstack-swagger-tab-content-gap: 14px; +} + +.dstack-swagger-ui .swagger-ui, +.dstack-swagger-ui .swagger-ui :where(button, input, optgroup, select, textarea) { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); +} + +.dstack-swagger-ui .swagger-ui *, +.dstack-swagger-ui .swagger-ui *::before, +.dstack-swagger-ui .swagger-ui *::after { + box-shadow: none !important; + text-shadow: none !important; +} + +.dstack-swagger-ui .swagger-ui .information-container, +.dstack-swagger-ui .swagger-ui > .info { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .wrapper { + padding: 0 !important; +} + +.md-typeset .dstack-swagger-operation-anchor:not(.dstack-swagger-operation-title) { + font-size: 0; + height: 1px; + line-height: 0; + margin: 0 !important; + overflow: hidden; + padding: 0 !important; + pointer-events: none; + position: absolute; + scroll-margin-top: 0; + top: calc(-1 * var(--dstack-swagger-anchor-offset)); + visibility: hidden; + width: 1px; +} + +.md-typeset + .dstack-swagger-operation-anchor:not(.dstack-swagger-operation-title) + .headerlink { + display: none; +} + +.md-typeset .dstack-swagger-ui .dstack-swagger-operation-title { + scroll-margin-top: var(--dstack-swagger-operation-anchor-offset); +} + +.dstack-swagger-ui .swagger-ui .scheme-container { + background: transparent; + box-shadow: none; + margin: 0; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .scheme-container .schemes-server-container .servers-title, +.dstack-swagger-ui .swagger-ui .scheme-container .schemes-server-container .servers label { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: 0.7rem; + font-weight: 400; + margin: 0 15px 0 0; +} + +.dstack-swagger-ui .swagger-ui .scheme-container .schemes-server-container .servers-title { + display: none; +} + +.dstack-swagger-ui .swagger-ui .scheme-container .schemes-server-container .servers label { + margin: 0; +} + +.dstack-swagger-ui .swagger-ui .scheme-container .schemes-server-container .servers select, +.dstack-swagger-ui .swagger-ui .content-type-wrapper select.content-type { + background-color: transparent; + border: 1px solid rgba(0, 0, 0, 0.87); + border-radius: 0; + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: var(--dstack-swagger-control-font-size); + font-weight: 400; + height: var(--dstack-swagger-control-height); + margin: 0; + padding-bottom: 0; + padding-top: 0; + text-transform: none; +} + +.dstack-swagger-ui .swagger-ui .scheme-container .schemes-server-container .servers select:hover, +.dstack-swagger-ui .swagger-ui .content-type-wrapper select.content-type:hover { + border-color: black; +} + +.dstack-swagger-ui .swagger-ui .opblock, +.dstack-swagger-ui .swagger-ui .opblock.opblock-delete, +.dstack-swagger-ui .swagger-ui .opblock.opblock-deprecated, +.dstack-swagger-ui .swagger-ui .opblock.opblock-get, +.dstack-swagger-ui .swagger-ui .opblock.opblock-head, +.dstack-swagger-ui .swagger-ui .opblock.opblock-options, +.dstack-swagger-ui .swagger-ui .opblock.opblock-patch, +.dstack-swagger-ui .swagger-ui .opblock.opblock-post, +.dstack-swagger-ui .swagger-ui .opblock.opblock-put, +.dstack-swagger-ui .swagger-ui .opblock.opblock-query { + background: transparent; + border: 0; + border-radius: 0; + margin: 0 0 36px; + position: relative; +} + +.dstack-swagger-ui .swagger-ui .opblock > .opblock-summary { + background: transparent; + box-sizing: border-box; + cursor: default; + padding: 10px 0; +} + +.dstack-swagger-ui .swagger-ui .opblock.dstack-swagger-has-try-out > .opblock-summary { + padding-right: calc(var(--dstack-swagger-try-out-width, 94px) + 8px); +} + +.dstack-swagger-ui .swagger-ui .opblock.is-open > .opblock-summary { + border-bottom: 0; +} + +.dstack-swagger-ui .swagger-ui .opblock > .opblock-summary:hover { + background: transparent; +} + +.dstack-swagger-ui .swagger-ui .opblock > .opblock-summary .opblock-summary-control { + align-items: center; + background: transparent; + cursor: default; + display: flex; + gap: 8px; + min-width: 0; + width: 100%; +} + +.dstack-swagger-ui .swagger-ui .opblock > .opblock-summary .opblock-control-arrow { + display: none; +} + +.dstack-swagger-ui .swagger-ui .opblock > .opblock-summary .authorization__btn { + display: none; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary .view-line-link.copy-to-clipboard { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path-description-wrapper { + align-items: center; + background: transparent; + border: 1px solid rgba(0, 0, 0, 0.87); + border-radius: 0; + color: var(--md-default-fg-color); + cursor: default !important; + display: block; + flex: 1 1 auto; + font-family: var(--md-text-font-family); + font-size: var(--dstack-swagger-control-font-size); + font-weight: 400; + gap: 0; + height: var(--dstack-swagger-control-height); + min-width: 0; + overflow: hidden; + padding: 0; + position: relative; + user-select: text; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-description { + display: none; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-summary-actions { + align-items: center; + display: flex; + flex: 0 0 auto; + gap: 8px; + margin-left: 8px; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-summary-actions .content-type-wrapper { + margin: 0; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-summary-actions .try-out__btn { + margin: 0; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-section-header.dstack-swagger-try-out-source-header { + display: block !important; + overflow: visible; +} + +.dstack-swagger-ui + .swagger-ui + .opblock + .opblock-section-header.dstack-swagger-try-out-source-header.dstack-swagger-empty-parameters { + height: 0; + min-height: 0; + padding: 0; +} + +.dstack-swagger-ui + .swagger-ui + .opblock + .opblock-section-header.dstack-swagger-try-out-source-header.dstack-swagger-empty-parameters + h4 { + display: none !important; +} + +.dstack-swagger-ui + .swagger-ui + .opblock + .opblock-section-header.dstack-swagger-try-out-source-header.dstack-swagger-empty-parameters + .tab-header { + display: none !important; +} + +.dstack-swagger-ui + .swagger-ui + .opblock + .opblock-section-header.dstack-swagger-try-out-source-header.dstack-swagger-empty-parameters + + .parameters-container.dstack-swagger-empty-parameters + + .opblock-section-request-body { + margin-top: 16px; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-section-header .try-out__btn.dstack-swagger-summary-try-out { + margin: 0; + position: absolute; + right: 0; + top: var(--dstack-swagger-try-out-top, 10px); + z-index: 5; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-header .dstack-swagger-original-control { + height: 1px !important; + margin: 0 !important; + opacity: 0 !important; + overflow: hidden !important; + padding: 0 !important; + pointer-events: none !important; + position: absolute !important; + width: 1px !important; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-delete .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-deprecated .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-get .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-head .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-options .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-patch .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-post .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-put .opblock-summary-method, +.dstack-swagger-ui .swagger-ui .opblock.opblock-query .opblock-summary-method { + background: transparent; + border: 1px solid rgba(0, 0, 0, 0.87); + border-radius: 0; + color: var(--md-default-fg-color); + display: inline-flex; + flex: 0 0 auto; + align-items: center; + justify-content: center; + font-family: var(--md-text-font-family); + font-size: var(--dstack-swagger-control-font-size); + font-weight: 400; + height: var(--dstack-swagger-control-height); + line-height: 1.2; + min-width: 72px; + padding: 0 0.65rem; + text-transform: none; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-operation-id, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path__deprecated { + color: var(--md-default-fg-color); + cursor: default !important; + font-family: var(--md-text-font-family); + font-weight: 400; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path__deprecated { + box-sizing: border-box; + display: block; + font-size: var(--dstack-swagger-control-font-size); + line-height: calc(var(--dstack-swagger-control-height) - 2px); + max-width: none; + min-width: 0; + overflow-x: auto; + overflow-y: hidden; + padding: 0 calc(var(--dstack-swagger-control-height) + 0.4rem) 0 0.65rem; + scrollbar-color: transparent transparent; + scrollbar-width: none; + text-overflow: clip; + white-space: nowrap; + word-break: normal; + overflow-wrap: normal; + -webkit-overflow-scrolling: touch; + user-select: text; + -webkit-user-select: text; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path *, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path__deprecated * { + white-space: nowrap; + word-break: normal; + overflow-wrap: normal; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path::-webkit-scrollbar, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path__deprecated::-webkit-scrollbar { + background: transparent; + height: 0; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path::-webkit-scrollbar-thumb, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path__deprecated::-webkit-scrollbar-thumb, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path::-webkit-scrollbar-track, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-path__deprecated::-webkit-scrollbar-track { + background: transparent; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-operation-id { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url *, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url:hover, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url:hover * { + cursor: default !important; +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url .opblock-summary-path, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url .opblock-summary-path *, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url .opblock-summary-path__deprecated, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-summary-url .opblock-summary-path__deprecated * { + cursor: text !important; +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy { + align-items: center; + align-self: stretch; + background: transparent; + border: 0; + color: black; + cursor: pointer !important; + display: inline-flex; + height: 100%; + justify-content: center; + margin: 0; + padding: 0; + position: absolute; + right: 0; + top: 0; + width: var(--dstack-swagger-control-height); +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy *, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy:hover, +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy:hover * { + cursor: pointer !important; +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy::before { + background: var(--dstack-swagger-border-color); + color: var(--md-default-bg-color); + content: "Copied to clipboard"; + display: block; + font-family: var(--md-text-font-family); + font-size: 0.6rem; + font-weight: 400; + line-height: 1.2; + opacity: 0; + padding: 0.2rem 0.35rem; + pointer-events: none; + position: absolute; + right: 0; + top: -1.6rem; + transform: translateY(0.2rem); + transition: + opacity 0.12s ease, + transform 0.12s ease; + white-space: nowrap; + z-index: 4; +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy.dstack-swagger-url-copy-copied::before { + opacity: 1; + transform: translateY(0); +} + +.dstack-swagger-ui .swagger-ui .opblock .dstack-swagger-url-copy::after { + background-color: currentColor; + content: ""; + display: block; + height: 1.125em; + mask-image: var(--md-code-copy-icon, var(--md-clipboard-icon)); + mask-position: center; + mask-repeat: no-repeat; + mask-size: contain; + width: 1.125em; + -webkit-mask-image: var(--md-code-copy-icon, var(--md-clipboard-icon)); + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: contain; +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-description, +.dstack-swagger-ui .swagger-ui .opblock .opblock-section-header h4, +.dstack-swagger-ui .swagger-ui .opblock .opblock-section-header > label, +.dstack-swagger-ui .swagger-ui .parameters-col_description, +.dstack-swagger-ui .swagger-ui .parameters-col_name, +.dstack-swagger-ui .swagger-ui .response-col_description, +.dstack-swagger-ui .swagger-ui .response-col_status, +.dstack-swagger-ui .swagger-ui .responses-inner h4:not(.dstack-swagger-response-title), +.dstack-swagger-ui .swagger-ui .responses-inner h5, +.dstack-swagger-ui .swagger-ui table thead tr td, +.dstack-swagger-ui .swagger-ui table thead tr th { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); +} + +.dstack-swagger-ui .swagger-ui .opblock .opblock-section-header { + background: transparent; + border-bottom: 0; + border-top: 0; + min-height: 0; + padding: 0; +} + +.dstack-swagger-ui + .swagger-ui + .opblock + .opblock-section-header.dstack-swagger-empty-parameters:not(.dstack-swagger-try-out-source-header), +.dstack-swagger-ui .swagger-ui .opblock-section-request-body > .opblock-section-header.dstack-swagger-request-body-header-hidden, +.dstack-swagger-ui .swagger-ui .dstack-swagger-native-response-caption, +.dstack-swagger-ui .swagger-ui .responses-wrapper > .opblock-section-header, +.dstack-swagger-ui .swagger-ui .responses-inner > h4:not(.dstack-swagger-response-title), +.dstack-swagger-ui .swagger-ui .responses-inner > h5, +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses > .opblock-section-header, +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-inner > h4:not(.dstack-swagger-response-title), +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-inner > h5, +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-table > thead, +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .responses-table + > tbody + > tr.response + > .response-col_status, +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .responses-table + > tbody + > tr.response + > .response-col_links, +.dstack-swagger-ui .swagger-ui .parameters-container.dstack-swagger-empty-parameters, +.dstack-swagger-ui .swagger-ui .execute-wrapper:empty { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .curl-command, +.dstack-swagger-ui .swagger-ui .request-url { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-table, +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-table > tbody { + display: block; + width: 100%; +} + +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses { + margin-bottom: 12px; +} + +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-table { + margin: 0; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .responses-table + > tbody + > tr.response { + background: transparent; + border: 0; + display: block; + margin: 0 0 1em; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .responses-table + > tbody + > tr.response + > td { + border: 0; + box-sizing: border-box; + display: block; + max-width: none; + padding: 0; + width: 100%; +} + +.md-typeset + .dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-container:not(blockquote) { + margin: 0; +} + +.md-typeset + .dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-section + > h4.dstack-swagger-response-title { + color: rgb(0, 0, 0); + font-family: var(--md-text-font-family); + font-size: 19.5px; + font-weight: 800; + line-height: 26px; + margin: 1em 0; + padding: 0; + text-transform: none; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-body + > .response-controls { + padding-top: 0; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-source { + display: none !important; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .model-example + > .tab + + :is(.highlight-code, .model-box, [role="tabpanel"]) { + margin-top: 20px; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-example { + margin-top: 1em; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-example + > .tab { + margin-top: 0; +} + +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-example-panel, +.dstack-swagger-ui + .swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-schema-panel { + margin-top: 20px; +} + +[dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-container + :is(pre, .admonition, details, .termy) { + margin-left: 0 !important; +} + +[dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > summary.dstack-swagger-response-title { + display: block; + list-style: none; +} + +[dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > .dstack-swagger-response-body { + padding-left: 32px; +} + +[dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > summary.dstack-swagger-response-title::-webkit-details-marker { + display: none; +} + +[dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > summary.dstack-swagger-response-title::marker { + content: ""; +} + +@media screen and (max-width: 44.9375em) { + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition { + border-width: 0; + border-radius: 0 !important; + margin-left: -0.8rem !important; + margin-right: -0.8rem !important; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > .dstack-swagger-response-body { + padding-left: 0; + padding-right: 0; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + :is(.admonition, details, blockquote) { + margin-left: 0 !important; + margin-right: 0 !important; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + :is( + .dstack-swagger-response-example, + .dstack-swagger-request-example, + .model-example, + .highlight, + .highlight-code, + .termy, + .dstack-swagger-json-editor, + .dstack-swagger-request-editor, + .model-box, + .md-typeset__scrollwrap, + .md-typeset__table + ) { + box-sizing: border-box; + margin-left: -0.8rem !important; + margin-right: -0.8rem !important; + max-width: calc(100% + 1.6rem); + width: calc(100% + 1.6rem); + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > .dstack-swagger-response-body + > .dstack-swagger-response-example { + margin-left: calc(50% - 50vw) !important; + margin-right: calc(50% - 50vw) !important; + max-width: 100vw; + width: 100vw; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > .dstack-swagger-response-body + > .dstack-swagger-response-example + > :is(.tab, [role="tabpanel"]) { + margin-left: 0 !important; + margin-right: 0 !important; + max-width: 100%; + width: 100%; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + > .dstack-swagger-response-body + > .dstack-swagger-response-example + > [role="tabpanel"] + > :is(.highlight, .highlight-code, .dstack-swagger-json-editor) { + margin-left: 0 !important; + margin-right: 0 !important; + max-width: 100%; + width: 100%; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .responses-wrapper.dstack-swagger-responses + .dstack-swagger-response-admonition + :is(pre, .highlight, .highlight-code, .termy, .md-typeset__table) { + box-sizing: border-box; + max-width: 100%; + min-width: 0; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .swagger-ui + :is(.dstack-swagger-response-example, .dstack-swagger-request-example, .model-example) { + box-sizing: border-box; + margin-left: -0.8rem !important; + margin-right: -0.8rem !important; + max-width: calc(100% + 1.6rem); + width: calc(100% + 1.6rem); + } + + [dir=ltr] .md-typeset .dstack-swagger-ui .swagger-ui :is(.tab, .tab-header) { + box-sizing: border-box; + display: flex; + flex-wrap: nowrap; + max-width: 100%; + overflow-x: auto; + overflow-y: hidden; + width: 100%; + -webkit-overflow-scrolling: touch; + } + + [dir=ltr] .md-typeset .dstack-swagger-ui .swagger-ui :is(.tab li, .tab-header .tab-item) { + flex: 0 0 auto; + white-space: nowrap; + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .swagger-ui + :is(.dstack-swagger-json-editor, .dstack-swagger-request-editor) { + border-radius: 0 !important; + box-sizing: border-box; + margin-left: -0.8rem !important; + margin-right: -0.8rem !important; + max-width: calc(100% + 1.6rem); + width: calc(100% + 1.6rem); + } + + [dir=ltr] + .md-typeset + .dstack-swagger-ui + .swagger-ui + :is(.dstack-swagger-json-editor, .dstack-swagger-request-editor) + :is(pre, code, textarea) { + box-sizing: border-box; + max-width: 100%; + min-width: 0; + } +} + +.dstack-swagger-ui .swagger-ui .tab-header { + background: -webkit-linear-gradient(45deg, rgba(0, 42, 255, 0.025), rgb(0 114 255 / 0.25%), rgba(0, 42, 255, 0.0125)); + border: 0.5px solid rgba(0, 0, 0, 0.5); + border-radius: 0; + display: inline-flex; + height: 100%; + list-style: none !important; + margin: 16px 0 -3px; + overflow: visible; + padding: 0; + position: relative; + z-index: 1; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-header + .parameters-container { + margin-top: 28px; +} + +.dstack-swagger-ui .swagger-ui .parameters-container:not(.dstack-swagger-empty-parameters) { + margin-bottom: 28px; +} + +.dstack-swagger-ui .swagger-ui .tab-header::before { + content: none; + display: none; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item { + display: inline-block; + margin: 0 -1px 0 0; + padding: 0 !important; + position: relative; + z-index: 2; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item:last-child { + margin-right: -2px; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item + .tab-item::before { + background: rgba(0, 0, 0, 0.25); + bottom: 0; + content: ""; + display: block; + left: 0; + position: absolute; + top: 0; + width: 0.5px; + z-index: 5; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item.active::before, +.dstack-swagger-ui .swagger-ui .tab-header .tab-item.active + .tab-item::before { + display: block; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item h4 { + margin: 0; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item h4 span { + border: 1px solid transparent; + border-radius: 0; + color: rgba(0, 0, 0, 0.6); + display: block; + font-family: var(--md-text-font-family); + font-size: 16.5px !important; + font-weight: 700 !important; + line-height: 1.2 !important; + min-width: 80px; + padding: 18px 18px 16px !important; + position: relative; + text-align: center; + transition: + background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1), + color 0.25s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 3; +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item.active h4 span { + background: var(--dstack-swagger-quote-bg); + border-color: black; + border-style: dotted; + color: var(--md-default-fg-color); +} + +.dstack-swagger-ui .swagger-ui .tab-header .tab-item h4 span::after, +.dstack-swagger-ui .swagger-ui .opblock .tab-header .tab-item.active h4 span::after { + content: none !important; + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .opblock-description-wrapper, +.dstack-swagger-ui .swagger-ui .opblock-external-docs-wrapper, +.dstack-swagger-ui .swagger-ui .opblock-title_normal, +.dstack-swagger-ui .swagger-ui .parameters-container, +.dstack-swagger-ui .swagger-ui .responses-inner { + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .responses-wrapper.dstack-swagger-responses .responses-inner { + padding-top: 16px; +} + +.dstack-swagger-ui .swagger-ui .opblock > .opblock-description-wrapper { + margin-bottom: 16px; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-source-operation-description { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .markdown, +.dstack-swagger-ui .swagger-ui .renderedMarkdown, +.dstack-swagger-ui .swagger-ui .opblock .opblock-summary-description, +.dstack-swagger-ui .swagger-ui .opblock-description-wrapper, +.dstack-swagger-ui .swagger-ui .opblock-external-docs-wrapper, +.dstack-swagger-ui .swagger-ui .parameters-col_description, +.dstack-swagger-ui .swagger-ui .response-col_description, +.dstack-swagger-ui .swagger-ui .response-col_links { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: 0.8rem; + font-weight: 400; + line-height: 1.3rem; +} + +.dstack-swagger-ui .swagger-ui .markdown p, +.dstack-swagger-ui .swagger-ui .renderedMarkdown p, +.dstack-swagger-ui .swagger-ui .opblock-description-wrapper p, +.dstack-swagger-ui .swagger-ui .opblock-external-docs-wrapper p, +.dstack-swagger-ui .swagger-ui .parameters-col_description p, +.dstack-swagger-ui .swagger-ui .response-col_description p { + color: inherit; + font-family: inherit; + font-size: inherit; + font-weight: inherit; + line-height: inherit; + margin: 1em 0; +} + +.dstack-swagger-ui .swagger-ui .markdown p:first-child, +.dstack-swagger-ui .swagger-ui .renderedMarkdown p:first-child, +.dstack-swagger-ui .swagger-ui .opblock-description-wrapper p:first-child, +.dstack-swagger-ui .swagger-ui .opblock-external-docs-wrapper p:first-child, +.dstack-swagger-ui .swagger-ui .parameters-col_description p:first-child, +.dstack-swagger-ui .swagger-ui .response-col_description p:first-child { + margin-top: 0; +} + +.dstack-swagger-ui .swagger-ui .markdown p:last-child, +.dstack-swagger-ui .swagger-ui .renderedMarkdown p:last-child, +.dstack-swagger-ui .swagger-ui .opblock-description-wrapper p:last-child, +.dstack-swagger-ui .swagger-ui .opblock-external-docs-wrapper p:last-child, +.dstack-swagger-ui .swagger-ui .parameters-col_description p:last-child, +.dstack-swagger-ui .swagger-ui .response-col_description p:last-child { + margin-bottom: 0; +} + +.dstack-swagger-ui .swagger-ui .parameters, +.dstack-swagger-ui .swagger-ui .responses-table { + border-collapse: collapse; +} + +.dstack-swagger-ui .swagger-ui .table-container { + padding: 0 !important; +} + +.dstack-swagger-ui .swagger-ui .parameters { + border-collapse: collapse; + display: block; + width: 100%; +} + +.dstack-swagger-ui .swagger-ui .parameters > tbody { + display: grid; + gap: 14px; + width: 100%; +} + +.dstack-swagger-ui .swagger-ui .parameters > tbody > tr { + align-items: start; + column-gap: 16px; + display: grid; + grid-template-columns: minmax(180px, 32%) minmax(0, 1fr); +} + +.dstack-swagger-ui .swagger-ui .parameters > tbody > tr > td { + display: block; + min-width: 0; + width: auto !important; +} + +.dstack-swagger-ui .swagger-ui .parameters > thead { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui table { + padding: 0 !important; +} + +.dstack-swagger-ui .swagger-ui .parameters thead tr th, +.dstack-swagger-ui .swagger-ui .parameters tbody tr td, +.dstack-swagger-ui .swagger-ui .responses-table > thead > tr > td, +.dstack-swagger-ui .swagger-ui .responses-table > tbody > tr.response > td { + border-bottom: 0; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .responses-table > tbody > tr.response:last-child > td { + border-bottom: 0; +} + +.dstack-swagger-ui .swagger-ui .parameters-col_name, +.dstack-swagger-ui .swagger-ui .parameters-col_description { + max-width: none; + padding-left: 0 !important; + padding-right: 0 !important; + vertical-align: top; +} + +.dstack-swagger-ui .swagger-ui .parameters-col_name { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: 0.8rem; + line-height: 1.3rem; +} + +.dstack-swagger-ui .swagger-ui .parameters-col_description { + margin-bottom: 0; + width: auto; +} + +.dstack-swagger-ui .swagger-ui .parameters-col_description .json-schema-form, +.dstack-swagger-ui .swagger-ui .parameters-col_description .json-schema-form-item { + margin: 0; + max-width: none; + width: 100%; +} + +.dstack-swagger-ui + .swagger-ui + .parameters-col_description + > :is(.markdown, .renderedMarkdown, p):not(:empty) + + :is(.json-schema-form, .json-schema-form-item, input, select, textarea) { + margin-top: 8px; +} + +.dstack-swagger-ui .swagger-ui .parameters-col_description .parameter__default { + display: none; +} + +.dstack-swagger-ui .swagger-ui .parameter__name, +.dstack-swagger-ui .swagger-ui .parameter__type, +.dstack-swagger-ui .swagger-ui .parameter__in, +.dstack-swagger-ui .swagger-ui .parameter__deprecated { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-weight: 400; + line-height: inherit; +} + +.dstack-swagger-ui .swagger-ui .parameter__name { + background: var(--md-code-bg-color); + color: var(--md-default-fg-color); + display: inline; + font-family: var(--md-code-font-family); + font-size: 0.7rem; + font-weight: 500; + padding: 0.05rem 0.22rem; +} + +.dstack-swagger-ui .swagger-ui .parameter__name.required span { + display: none; +} + +.dstack-swagger-ui .swagger-ui .parameter__name.required::after { + content: "*"; + color: var(--md-default-fg-color); + font-size: inherit; + font-weight: inherit; + padding: 0 0 0 0.05rem; + position: static; + vertical-align: baseline; +} + +.dstack-swagger-ui .swagger-ui .parameter__type, +.dstack-swagger-ui .swagger-ui .parameter__in, +.dstack-swagger-ui .swagger-ui .parameter__deprecated { + color: rgba(0, 0, 0, 0.6); + display: inline; + font-size: 0.7rem; + margin: 0 0 0 0.35rem; +} + +.dstack-swagger-ui .swagger-ui .parameter__type .prop-format, +.dstack-swagger-ui .swagger-ui .parameter__in .prop-format { + color: inherit; + font-family: inherit; + font-size: inherit; + font-weight: inherit; +} + +.dstack-swagger-ui .swagger-ui .btn, +.dstack-swagger-ui .swagger-ui select, +.dstack-swagger-ui .swagger-ui input[type=email], +.dstack-swagger-ui .swagger-ui input[type=file], +.dstack-swagger-ui .swagger-ui input[type=password], +.dstack-swagger-ui .swagger-ui input[type=search], +.dstack-swagger-ui .swagger-ui input[type=text], +.dstack-swagger-ui .swagger-ui textarea { + background-color: transparent; + border: var(--dstack-swagger-hairline-border); + border-radius: 0; + color: var(--md-default-fg-color); +} + +.dstack-swagger-ui .swagger-ui .parameters input[type=text], +.dstack-swagger-ui .swagger-ui .parameters input[type=number], +.dstack-swagger-ui .swagger-ui .parameters select, +.dstack-swagger-ui .swagger-ui .parameters textarea { + background-color: transparent; + border: 1px solid rgba(0, 0, 0, 0.87); + border-radius: 0; + box-sizing: border-box; + color: var(--md-default-fg-color); + cursor: default; + font-family: var(--md-text-font-family); + font-size: var(--dstack-swagger-control-font-size); + font-weight: 400; + height: var(--dstack-swagger-control-height); + line-height: calc(var(--dstack-swagger-control-height) - 2px); + margin: 0; + max-width: none; + min-height: var(--dstack-swagger-control-height); + padding: 0 0.65rem; + width: 100%; +} + +@media screen and (max-width: 44em) { + .dstack-swagger-ui .swagger-ui .parameters > tbody > tr { + gap: 6px; + grid-template-columns: minmax(0, 1fr); + } +} + +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters input[type=text], +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters input[type=number], +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters textarea { + cursor: text; +} + +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters select { + cursor: pointer; +} + +.dstack-swagger-ui .swagger-ui .parameters textarea { + height: auto; + line-height: 1.3rem; + min-height: calc(var(--dstack-swagger-control-height) * 3); + padding-bottom: 0.45rem; + padding-top: 0.45rem; +} + +.dstack-swagger-ui .swagger-ui .parameters input::placeholder, +.dstack-swagger-ui .swagger-ui .parameters textarea::placeholder { + color: rgba(0, 0, 0, 0.45); + opacity: 1; +} + +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters input[type=text]:hover, +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters input[type=number]:hover, +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters select:hover, +.dstack-swagger-ui .swagger-ui .opblock.is-open .parameters textarea:hover { + border-color: black; +} + +.dstack-swagger-ui .swagger-ui :where(a, button, input, select, textarea, [role="button"], .btn, .model-box-control, .opblock-summary-control):focus, +.dstack-swagger-ui .swagger-ui :where(a, button, input, select, textarea, [role="button"], .btn, .model-box-control, .opblock-summary-control):focus-visible { + box-shadow: none !important; + outline: none !important; +} + +.dstack-swagger-ui .swagger-ui .btn { + font-weight: 500; + padding: 0 0.85rem; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-hidden-reset { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .btn.authorize { + align-items: center; + background: var(--dstack-swagger-border-color); + border: var(--dstack-swagger-primary-border); + border-radius: 0; + color: var(--md-default-bg-color); + display: inline-flex; + font-size: var(--dstack-swagger-control-font-size); + font-weight: 500 !important; + gap: 8px; + height: var(--dstack-swagger-control-height); + line-height: 1.2; + padding: 0 0.85rem; + white-space: nowrap; +} + +.dstack-swagger-ui .swagger-ui .btn.authorize:hover { + background: black; + border-color: black; + color: var(--md-default-bg-color); +} + +.dstack-swagger-ui .swagger-ui .try-out__btn, +.dstack-swagger-ui .swagger-ui .btn.execute, +.dstack-swagger-ui .swagger-ui .dstack-swagger-clear-btn, +.dstack-swagger-ui .swagger-ui .btn.cancel { + align-items: center; + border: var(--dstack-swagger-primary-border); + border-radius: 0; + display: inline-flex; + font-size: var(--dstack-swagger-control-font-size); + font-weight: 500 !important; + gap: 8px; + height: var(--dstack-swagger-control-height); + justify-content: center; + line-height: 1.2; + min-width: 6rem; + padding: 0 0.85rem; + white-space: nowrap; + width: auto; +} + +.dstack-swagger-ui .swagger-ui .try-out__btn, +.dstack-swagger-ui .swagger-ui .dstack-swagger-clear-btn, +.dstack-swagger-ui .swagger-ui .btn.cancel { + background: transparent; + color: var(--md-default-fg-color); +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-clear-btn, +.dstack-swagger-ui .swagger-ui .try-out__btn.cancel, +.dstack-swagger-ui .swagger-ui .btn.cancel { + border-radius: var(--dstack-swagger-action-radius); + min-width: 0; +} + +.dstack-swagger-ui .swagger-ui .try-out__btn:hover, +.dstack-swagger-ui .swagger-ui .dstack-swagger-clear-btn:hover, +.dstack-swagger-ui .swagger-ui .btn.cancel:hover { + background: transparent; + border-color: black; + color: black; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-wrapper, +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-row { + align-items: center; + display: flex; + gap: 8px; + margin-bottom: 16px !important; + margin-top: 16px !important; + padding: 0 !important; + width: 100%; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-wrapper { + margin-top: 16px !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-wrapper > .dstack-swagger-execute-row { + margin-bottom: 0 !important; + margin-top: 0 !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-row > .btn { + align-items: center; + background: transparent !important; + border: var(--dstack-swagger-primary-border) !important; + border-radius: var(--dstack-swagger-action-radius) !important; + box-sizing: border-box; + color: var(--md-default-fg-color) !important; + display: inline-flex; + font-size: var(--dstack-swagger-control-font-size); + font-weight: 500 !important; + height: var(--dstack-swagger-control-height) !important; + justify-content: center; + line-height: 1.2; + overflow: visible; + padding: 0 0.85rem !important; + text-overflow: clip; + white-space: nowrap; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-row > .btn.execute { + flex: 1 1 auto !important; + min-width: 0 !important; + width: auto !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-row > .btn:not(.execute) { + flex: 0 0 auto !important; + min-width: 5rem !important; + width: auto !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-row > .btn:hover { + background: transparent !important; + border-color: black !important; + color: black !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-wrapper ~ .responses-wrapper, +.dstack-swagger-ui .swagger-ui .dstack-swagger-execute-row ~ .responses-wrapper { + margin-top: 0 !important; +} + +.dstack-swagger-ui .swagger-ui .download-contents { + background: var(--dstack-swagger-border-color); + border: var(--dstack-swagger-primary-border); + border-radius: 0; + color: var(--md-default-bg-color); +} + +.dstack-swagger-ui .swagger-ui .btn.authorize span { + float: none; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .btn.authorize svg { + flex: 0 0 auto; + fill: currentColor; + height: 16px; + margin: 0; + order: -1; + width: 16px; +} + +.dstack-swagger-ui .swagger-ui .btn.authorize svg path { + fill: currentColor; +} + +.dstack-swagger-ui .swagger-ui .highlight-code > .microlight, +.dstack-swagger-ui .swagger-ui .opblock-body pre.microlight { + background: var(--dstack-swagger-code-bg-color) !important; + border: 0; + border-radius: 0; + color: var(--md-code-fg-color) !important; + font-family: var(--md-code-font-family); + font-size: var(--dstack-code-font-size); + font-weight: 400; + line-height: var(--dstack-code-line-height); + margin: 0; + overflow: auto; + padding: 65px 30px 35px 40px; + position: relative; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-request-editor { + --dstack-editable-code-max-height: var(--dstack-swagger-schema-code-max-height); + margin: 0; +} + +.dstack-swagger-ui + .swagger-ui + .model-example.dstack-swagger-edit-schema-active + > :not(.tab):not(.dstack-swagger-edit-request-schema) { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-edit-request-schema[hidden] { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .highlight-code > .microlight::before, +.dstack-swagger-ui .swagger-ui .opblock-body pre.microlight::before { + background: #d9515d; + border-radius: 50%; + content: ""; + display: inline-block; + height: 12px; + left: 15px; + position: absolute; + top: 15px; + width: 12px; + -webkit-box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930 !important; + box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930 !important; +} + +.dstack-swagger-ui .swagger-ui textarea:not(.curl) { + background: transparent !important; + border: 0; + border-radius: 0; + box-sizing: border-box; + color: var(--md-code-fg-color) !important; + font-family: var(--md-code-font-family); + font-size: var(--dstack-code-font-size); + font-weight: 400; + line-height: var(--dstack-code-line-height); + margin: 0; + min-height: 220px; + padding: 65px 30px 35px 40px; + resize: vertical; + width: 100%; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-request-editor textarea:not(.curl) { + max-height: var(--dstack-editable-code-max-height); + min-height: 220px; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui textarea.curl { + background: transparent !important; + border: 1px solid rgba(0, 0, 0, 0.87); + border-radius: 0; + color: var(--md-default-fg-color) !important; + font-family: var(--md-text-font-family); + font-size: var(--dstack-swagger-control-font-size); + font-weight: 400; + line-height: calc(var(--dstack-swagger-control-height) - 2px); + min-height: var(--dstack-swagger-control-height); + padding: 0 0.65rem; + resize: none; + text-transform: none; +} + +.dstack-swagger-ui .swagger-ui .highlight-code > .microlight > code, +.dstack-swagger-ui .swagger-ui .opblock-body pre.microlight > code, +.md-typeset .dstack-swagger-ui .swagger-ui pre:not(.dstack-scrollable-code-pre) > code { + background: transparent !important; + color: inherit !important; + font-family: var(--md-code-font-family) !important; + padding: 0 !important; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-json-editor { + --dstack-scrollable-code-max-height: var(--dstack-swagger-schema-code-max-height); + margin: 0; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-json-editor > .md-code__nav { + background: transparent; + right: 15px; + top: 9px; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-json-editor .md-code__button { + background: transparent; + border: 0; + color: #a2a2a2; + height: 24px; + padding: 0; + width: 24px; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-json-editor .md-code__button::after { + height: 16px; + margin: 4px; + width: 16px; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-json-editor .md-code__button:hover, +.dstack-swagger-ui .swagger-ui .dstack-swagger-json-editor .md-code__button:focus-visible { + color: #eee; + outline: none; +} + +.dstack-swagger-ui .swagger-ui .model-box, +.dstack-swagger-ui .swagger-ui .model-container, +.dstack-swagger-ui .swagger-ui .json-schema-2020-12, +.dstack-swagger-ui .swagger-ui .json-schema-2020-12 button, +.dstack-swagger-ui .swagger-ui section.models { + background: transparent; + border-radius: 0; +} + +.dstack-swagger-ui .swagger-ui .json-schema-2020-12 { + margin: 0 0 15px; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .json-schema-2020-12--embedded, +.dstack-swagger-ui .swagger-ui .model-box .json-schema-2020-12 { + border-left: 0; + margin: 0; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .json-schema-2020-12__constraint, +.dstack-swagger-ui .swagger-ui .json-schema-2020-12-keyword__value--warning, +.dstack-swagger-ui .swagger-ui .json-schema-2020-12-json-viewer__value--warning { + background: transparent; + border: 0; + border-radius: 0; + color: var(--md-default-fg-color); +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper, +.dstack-swagger-ui .swagger-ui .auth-container .errors { + background: var(--dstack-swagger-quote-bg); + border: 0; + border-radius: 0; + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: 0.8rem; + line-height: 1.3rem; + margin: 1em 0; + padding: 8px 25px; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper h4, +.dstack-swagger-ui .swagger-ui .auth-container .errors h4 { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: 0.8rem; + font-weight: 700; + line-height: 1.3rem; + margin: 0 0 0.45rem; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper small, +.dstack-swagger-ui .swagger-ui .auth-container .errors small, +.dstack-swagger-ui .swagger-ui .errors-wrapper span, +.dstack-swagger-ui .swagger-ui .auth-container .errors span { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: inherit; + line-height: inherit; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper ul, +.dstack-swagger-ui .swagger-ui .auth-container .errors ul { + margin: 0.45rem 0 0; + padding: 0; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper li, +.dstack-swagger-ui .swagger-ui .auth-container .errors li { + color: var(--md-default-fg-color); + font-family: var(--md-text-font-family); + font-size: inherit; + line-height: inherit; + margin: 0; + padding: 0; + position: static; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper li::before, +.dstack-swagger-ui .swagger-ui .auth-container .errors li::before { + content: none !important; + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper li + li, +.dstack-swagger-ui .swagger-ui .auth-container .errors li + li { + margin-top: 0.2rem; +} + +.dstack-swagger-ui .swagger-ui .errors-wrapper .errors__clear-btn { + display: none; +} + +.dstack-swagger-ui .swagger-ui .dialog-ux .modal-ux { + background: var(--md-default-bg-color); + border: 0; + border-radius: 0; +} + +.dstack-swagger-ui .swagger-ui .dialog-ux .modal-ux-header { + border-bottom: 0; +} + +.dstack-swagger-ui[data-openapi-tag] .swagger-ui .opblock-tag { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .model-hint { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .tab { + background: -webkit-linear-gradient(45deg, rgba(0, 42, 255, 0.025), rgb(0 114 255 / 0.25%), rgba(0, 42, 255, 0.0125)); + border: 0.5px solid rgba(0, 0, 0, 0.5); + border-radius: 0; + display: inline-flex; + height: 100%; + list-style: none !important; + margin: 16px 0 -3px; + overflow: unset; + padding: 0; + position: relative; + z-index: 1; +} + +.dstack-swagger-ui .swagger-ui .tab::before { + content: none; + display: none; +} + +.dstack-swagger-ui .swagger-ui .tab + [role="tabpanel"] { + margin-top: 16px; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .model-example { + margin-top: 8px; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .dstack-swagger-request-example { + margin-top: 8px; +} + +.dstack-swagger-ui .swagger-ui .opblock > .dstack-swagger-operation-curl-example { + margin-top: 20px; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-request-curl-termy > [data-termynal]::before { + -webkit-box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930 !important; + box-shadow: 20px 0 0 #f4c025, 40px 0 0 #3ec930 !important; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .model-example > .tab { + margin: 0; +} + +.dstack-swagger-ui + .swagger-ui + .opblock-section-request-body + .model-example + > .tab + ~ :is( + .body-param, + .highlight-code, + .model-box, + .dstack-editable-code, + .dstack-swagger-edit-request-schema + ) { + margin-top: var(--dstack-swagger-tab-content-gap) !important; +} + +.dstack-swagger-ui + .swagger-ui + .opblock-section-request-body + .model-example + :is(.dstack-swagger-request-editor, .dstack-swagger-edit-request-schema) { + margin-top: var(--dstack-swagger-tab-content-gap) !important; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .dstack-swagger-request-example > .tab { + margin: 0; +} + +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .dstack-swagger-request-curl-panel, +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .dstack-swagger-request-schema-panel { + margin-top: var(--dstack-swagger-tab-content-gap); +} + +.dstack-swagger-ui .swagger-ui .opblock-section-request-body .dstack-swagger-request-model-hidden { + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .tab li { + display: inline-block; + margin: 0 -1px 0 0; + padding: 0 !important; + position: relative; + z-index: 2; +} + +.dstack-swagger-ui .swagger-ui .tab li:last-of-type { + margin-right: -2px; +} + +.dstack-swagger-ui .swagger-ui .tab li:first-of-type::after { + content: none !important; + display: none !important; +} + +.dstack-swagger-ui .swagger-ui .tab li + li::before { + background: rgba(0, 0, 0, 0.25); + bottom: 0; + content: ""; + display: block; + height: auto; + left: 0; + margin: 0; + position: absolute; + top: 0; + width: 0.5px; + z-index: 5; +} + +.dstack-swagger-ui .swagger-ui .tab li.active::before, +.dstack-swagger-ui .swagger-ui .tab li.active + li::before { + display: block; +} + +.dstack-swagger-ui .swagger-ui .tab li button.tablinks { + border: 1px solid transparent; + border-radius: 0; + color: rgba(0, 0, 0, 0.6); + cursor: pointer; + display: block; + font-family: var(--md-text-font-family); + font-size: 16.5px !important; + font-weight: 700 !important; + line-height: 1.2 !important; + min-width: 80px; + padding: 18px 18px 16px !important; + position: relative; + text-align: center; + transition: + background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1), + color 0.25s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 4; +} + +.dstack-swagger-ui .swagger-ui .tab li button.tablinks:hover, +.dstack-swagger-ui .swagger-ui .tab li.active button.tablinks, +.dstack-swagger-ui .swagger-ui .tab li button.tablinks[aria-selected="true"] { + color: var(--md-default-fg-color); + outline: none; +} + +.dstack-swagger-ui .swagger-ui .tab li.active button.tablinks, +.dstack-swagger-ui .swagger-ui .tab li button.tablinks[aria-selected="true"] { + background: var(--dstack-swagger-quote-bg); + border-color: transparent; + border-style: dotted; + /* margin-right: 1px; */ +} + +.dstack-swagger-ui .swagger-ui .tab li button.tablinks:focus-visible { + color: var(--md-accent-fg-color); + outline: none; +} + +.md-typeset .dstack-swagger-ui .swagger-ui .tab > li:first-child::before { + content: none; + display: none; +} + +.dstack-swagger-ui .swagger-ui .dstack-swagger-model-label, +.dstack-swagger-ui .swagger-ui .dstack-swagger-model-inline-title { + color: var(--md-default-fg-color); + font-family: var(--md-code-font-family); + font-size: 12px; +} + +.dstack-swagger-ui .swagger-ui .markdown code, +.dstack-swagger-ui .swagger-ui .renderedMarkdown code, +.md-typeset .dstack-swagger-ui .swagger-ui pre:not(.dstack-scrollable-code-pre) > code { + font-size: 12px !important; +} diff --git a/mkdocs/assets/stylesheets/termynal.css b/mkdocs/assets/stylesheets/termynal.css index d1bd26039a..77a8eba359 100644 --- a/mkdocs/assets/stylesheets/termynal.css +++ b/mkdocs/assets/stylesheets/termynal.css @@ -17,7 +17,7 @@ } .small > [data-termynal] { - font-size: 14px; + font-size: var(--dstack-code-font-size); line-height: 1.4; } @@ -32,10 +32,11 @@ color: var(--color-text); /* font-size: 18px; */ font-size: 14px; + line-height: var(--dstack-code-line-height); /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ font-family: var(--md-code-font-family) !important; - border-radius: 0; - padding: 45px 25px 25px; + border-radius: 3px; + padding: 60px 12px 25px 25px; /*padding: 75px 45px 35px;*/ position: relative; -webkit-box-sizing: border-box; @@ -69,15 +70,116 @@ text-align: center; } -a[data-terminal-control] { - text-align: right; +[data-termynal].dstack-termy-scrollable { + overflow: visible; +} + +[data-termynal].dstack-termy-scrollable > [data-termynal-body] { + max-height: var(--dstack-termy-max-height); + overflow: auto; + scrollbar-color: #a2a2a2 rgba(255, 255, 255, 0.06); + scrollbar-width: thin; +} + +[data-termynal].dstack-termy-scrollable > [data-termynal-body]::-webkit-scrollbar { + height: 10px; + width: 10px; +} + +[data-termynal].dstack-termy-scrollable > [data-termynal-body]::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.06); +} + +[data-termynal].dstack-termy-scrollable > [data-termynal-body]::-webkit-scrollbar-thumb { + background: #a2a2a2; + background-clip: content-box; + border: 2px solid transparent; + border-radius: 8px; +} + +[data-termynal].dstack-termy-scrollable > [data-termynal-body]::-webkit-scrollbar-thumb:hover { + background: #e5e5e9; + background-clip: content-box; +} + +[data-termynal].dstack-termy-has-copy > a[data-terminal-control] { + box-sizing: border-box; + padding-right: 32px; +} + +[data-termynal] > .dstack-termy-copy { + -webkit-appearance: none; + appearance: none; + background: transparent; + border: 0; + color: var(--color-text-subtle); + cursor: pointer; + height: 24px; + padding: 0; + position: absolute; + right: 15px; + top: 9px; + width: 24px; + z-index: 2; +} + +[data-termynal] > .dstack-termy-copy:before { + background: rgb(21, 22, 29); + border: 1px solid var(--color-text-subtle); + color: var(--color-text); + content: 'Copied to clipboard'; + font-family: var(--md-text-font-family); + font-size: 11px; + line-height: 1; + opacity: 0; + padding: 5px 7px; + pointer-events: none; + position: absolute; + right: calc(100% + 8px); + top: 50%; + transform: translateY(-50%); + transition: opacity 0.15s ease; + white-space: nowrap; +} + +[data-termynal] > .dstack-termy-copy.dstack-termy-copy-copied:before { + opacity: 1; +} + +[data-termynal] > .dstack-termy-copy:after { + background-color: currentColor; + content: ''; display: block; + height: 16px; + margin: 4px; + mask-image: var(--md-code-copy-icon, var(--md-clipboard-icon)); + mask-position: center; + mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-image: var(--md-code-copy-icon, var(--md-clipboard-icon)); + -webkit-mask-position: center; + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: contain; + width: 16px; +} + +[data-termynal] > .dstack-termy-copy:hover, +[data-termynal] > .dstack-termy-copy:focus-visible { + color: var(--color-text); + outline: none; +} + +a[data-terminal-control] { color: #aebbff; + display: block; + margin-right: 0.5rem; + margin-top: 1rem; + text-align: right; } [data-ty] { display: block; - line-height: 2; + line-height: inherit; } [data-ty]:before { diff --git a/mkdocs/blog/posts/dstack-metrics.md b/mkdocs/blog/posts/dstack-metrics.md index 0e74f0c4d2..d3bf4ffa67 100644 --- a/mkdocs/blog/posts/dstack-metrics.md +++ b/mkdocs/blog/posts/dstack-metrics.md @@ -33,7 +33,7 @@ Similar to `kubectl top`, if a run consists of multiple jobs (such as distribute !!! info "HTTP API" In addition to the `dstack stats` CLI commands, metrics can be obtained via the - [`/api/project/{project_name}/metrics/job/{run_name}`](../../docs/reference/api/http/#operations-tag-metrics) HTTP endpoint. + [`/api/project/{project_name}/metrics/job/{run_name}`](../../docs/reference/http/metrics.md) HTTP endpoint. ## Why monitor GPU usage diff --git a/mkdocs/docs/concepts/projects.md b/mkdocs/docs/concepts/projects.md index bafded47cf..a1de7ffb96 100644 --- a/mkdocs/docs/concepts/projects.md +++ b/mkdocs/docs/concepts/projects.md @@ -66,4 +66,4 @@ You can find the command on the project’s settings page: ??? info "API" - In addition to the UI, managing projects, users, and user permissions can also be done via the [HTTP API](../reference/api/http/index.md). + In addition to the UI, managing projects, users, and user permissions can also be done via the [HTTP API](../reference/http/index.md). diff --git a/mkdocs/docs/concepts/services.md b/mkdocs/docs/concepts/services.md index a0e0de7936..d9c61ec54f 100644 --- a/mkdocs/docs/concepts/services.md +++ b/mkdocs/docs/concepts/services.md @@ -125,7 +125,7 @@ If you do not have a [gateway](gateways.md) created, the service endpoint will b ```shell $ curl http://localhost:3000/proxy/services/main/qwen36/v1/chat/completions \ -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer <dstack token>' \ + -H 'Authorization: Bearer <user token>' \ -d '{ "model": "Qwen/Qwen3.6-27B", "messages": [ @@ -143,7 +143,7 @@ The request and response format depends on the serving framework used by the service. Even for OpenAI-compatible endpoints, the format may vary slightly across frameworks. -If [authorization](#authorization) is not disabled, the service endpoint requires the `Authorization` header with `Bearer `. +If [authorization](#authorization) is not disabled, the service endpoint requires the `Authorization` header with `Bearer `. ## Configuration options @@ -173,7 +173,7 @@ If you have a [gateway](gateways.md) created, the service endpoint will be acces ```shell $ curl https://llama31.example.com/v1/chat/completions \ -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer <dstack token>' \ + -H 'Authorization: Bearer <user token>' \ -d '{ "model": "meta-llama/Meta-Llama-3.1-8B-Instruct", "messages": [ diff --git a/mkdocs/docs/examples/accelerators/amd.md b/mkdocs/docs/examples/accelerators/amd.md index 26f255f280..7fbfb80729 100644 --- a/mkdocs/docs/examples/accelerators/amd.md +++ b/mkdocs/docs/examples/accelerators/amd.md @@ -93,8 +93,15 @@ Here are examples of a [service](../../concepts/services.md) that deploy !!! info "Docker image" - AMD deployments require specifying an image that already includes ROCm - drivers. The SGLang and vLLM examples above use pinned ROCm images. + AMD workloads require specifying an image with ROCm-compatible userspace and + framework packages. The SGLang and vLLM examples above use pinned ROCm + images. + + If you already have a ROCm-compatible image, use it. Otherwise, choose an + image for the framework you use from + [ROCm Docker images](https://hub.docker.com/u/rocm), e.g. `rocm/sgl-dev` + for SGLang, `rocm/vllm` for vLLM, or `rocm/pytorch` for PyTorch. For + generic AMD dev environments or tasks, use `rocm/dev-ubuntu-24.04`. To request multiple GPUs, specify the quantity after the GPU name, separated by a colon, e.g., `MI300X:4`. diff --git a/mkdocs/docs/examples/accelerators/tenstorrent.md b/mkdocs/docs/examples/accelerators/tenstorrent.md index 8344ced187..69971e6895 100644 --- a/mkdocs/docs/examples/accelerators/tenstorrent.md +++ b/mkdocs/docs/examples/accelerators/tenstorrent.md @@ -98,7 +98,7 @@ at `/proxy/services///`. ```shell $ curl http://127.0.0.1:3000/proxy/services/main/tt-inference-server/v1/chat/completions \ -X POST \ - -H 'Authorization: Bearer <dstack token>' \ + -H 'Authorization: Bearer <user token>' \ -H 'Content-Type: application/json' \ -d '{ "model": "meta-llama/Llama-3.2-1B-Instruct", diff --git a/mkdocs/docs/examples/inference/nim.md b/mkdocs/docs/examples/inference/nim.md index f7d1c03edf..3ec4c8b43c 100644 --- a/mkdocs/docs/examples/inference/nim.md +++ b/mkdocs/docs/examples/inference/nim.md @@ -71,7 +71,7 @@ If no gateway is created, the service endpoint will be available at ` See [installation](../installation.md#cli) on how to install the CLI. + +### Configuration + +The CLI requires a [project](../concepts/projects.md) configuration with the project name, server URL, and user token in `~/.dstack/config.yml`. + +
+ +```yaml +projects: + - name: main + url: http://127.0.0.1:3000 + token: + default: true + - name: octocat + url: https://sky.dstack.ai + token: +``` + +
+ +Use [`dstack project`](../reference/cli/dstack/project.md) to list, +[add](../installation.md#configure-the-project), delete, and set the default +project configurations. To run a command against a non-default project, pass +`--project NAME`, or set `DSTACK_PROJECT` in the current shell. + +??? info "Projects" + [Projects](../concepts/projects.md) enable the isolation of different teams and their resources. Users can be added to projects and assigned roles. Each user has a user token for authentication. + +### Manage fleets + +Before submitting runs, you must create at least one +[fleet](../concepts/fleets.md). Fleets act as both pools of instances and +templates for how those instances are provisioned. + +Use [`dstack fleet`](../reference/cli/dstack/fleet.md#dstack-fleet-list) to +list existing fleets, their configurations, and instances (if any): + +
+ +```shell +$ dstack fleet +``` + +
+ +??? info "Offers" + Offers are available instance configurations that match resource + requirements. + +
+ + ```shell + $ dstack offer --gpu H100 --max-offers 10 + ``` + +
+ + If no fleet is specified, + [`dstack offer`](../reference/cli/dstack/offer.md) shows offers from all + configured backends. + + Use `--fleet NAME` to restrict offers to a fleet. Listing offers does not + create capacity. + +Define a fleet configuration in a YAML file. The filename must end with +`.dstack.yml`, for example `fleet.dstack.yml`: + +
+ +```yaml +type: fleet +name: default + +nodes: 0..1 +idle_duration: 1h + +resources: + gpu: 0 +``` + +
+ +Pass the fleet configuration to `dstack apply`: + +
+ +```shell +$ dstack apply -f fleet.dstack.yml +``` + +
+ +If the `nodes` range starts with `0`, `dstack` creates a fleet template. +Instances are provisioned when matching runs are submitted. + +### Submit runs + +To submit a run, define a +[dev environment](../concepts/dev-environments.md), +[task](../concepts/tasks.md), or [service](../concepts/services.md) +configuration. The example below submits a task. + +
+ +```yaml +type: task +name: hello + +commands: + - echo hello world +``` + +
+ +Submit the run: + +
+ +```shell +$ dstack apply -f .dstack.yml +``` + +
+ +!!! info "Plan and confirmation" + `dstack apply` shows the plan and asks for confirmation before submitting + the run. To only see the plan, answer `n` at the prompt: + +
+ + ```shell + $ echo "n" | dstack apply -f .dstack.yml + ``` + +
+ + Use `-y` to skip confirmation. + +!!! info "Attached by default" + For run configurations, `dstack apply` automatically attaches after + submitting the run. This streams logs, forwards declared ports, and + configures SSH access. See [Attach to runs](#attach-to-runs). + + Use `-d` to submit in detached mode. + +### Attach to runs + +If the run was submitted with `-d`, or if you need to attach to another job in +a multi-job run, use `dstack attach`: + +
+ +```shell +$ dstack attach <run name> +``` + +
+ +!!! info "SSH" + During `dstack apply` in attached mode and during + `dstack attach `, the CLI downloads the current user's built-in + private SSH key if needed and stores it under `~/.dstack/ssh/`. + + While attached, the CLI updates `~/.dstack/ssh/config` with the run name as + an SSH host alias and ensures this file is included from `~/.ssh/config`: + +
+ + ```ssh-config + Host <run name> + HostName localhost + Port <local SSH port> + User root + IdentityFile ~/.dstack/ssh/<key> + IdentitiesOnly yes + ``` + +
+ + For VM-based and SSH fleets, `dstack` may also configure the + `-host` alias for SSH access to the host. + + While attached, connect to the run with: + +
+ + ```shell + $ ssh <run name> + ``` + +
+ +Use `--job JOB_NUMBER` with `dstack attach` to attach to another job. Ports +declared in the run configuration are forwarded while attached. + +??? info "User SSH keys" + The server stores a built-in SSH key pair for each user. + + Users can add custom public SSH keys via the UI or the + [users](../reference/http/users.md) API. To use a custom private key for a + particular run, pass `--ssh-identity` to `dstack apply` or `dstack attach`. + +### Browse logs + +When `dstack apply` is attached, it streams logs for job `0` automatically. +Use [`dstack logs`](../reference/cli/dstack/logs.md) to view logs in detached +mode, or to view logs for a specific job: + +
+ +```shell +$ dstack logs <run name> +``` + +
+ +Use `--job JOB_NUMBER` to select a job and `--since` to filter by time. + +??? info "Attached logs" + Use `--logs` with `dstack attach` to stream logs while attaching: + +
+ + ```shell + $ dstack attach <run name> --logs + ``` + +
+ +### Commands + +Other common CLI commands include [`dstack ps`](../reference/cli/dstack/ps.md), +[`dstack stop`](../reference/cli/dstack/stop.md), and +[`dstack event`](../reference/cli/dstack/event.md). + +!!! info "Verbose and JSON modes" + Use `-v` for more details where supported. For automation, use `--json`, + e.g. `dstack ps --json`, `dstack run get --json`, or + `dstack fleet get --json`. + +## API + +The `dstack` API is represented by the HTTP API. Use it for functionality not +available in the CLI or for integrations that need to call the server directly. + + + +### Authenticate + +The HTTP API requires the `Authorization` header for user authentication: + +```text +Authorization: Bearer +``` + +### Manage fleets + +The [fleets](../reference/http/fleets.md) API can list existing fleets, their +configurations, and instances (if any): + +
+ +```shell +$ curl "<server URL>/api/project/<project name>/fleets/list" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{"include_imported": true}' +``` + +
+ +??? info "Offers" + To check available offers via the HTTP API, call + [`/runs/get_plan`](../reference/http/runs.md) with the same lightweight + task specification used by `dstack offer`: + +
+ + ```shell + $ curl "<server URL>/api/project/<project name>/runs/get_plan" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{ + "run_spec": { + "configuration": { + "type": "task", + "commands": [":"], + "image": "scratch", + "user": "root", + "resources": { + "gpu": 0 + } + } + }, + "max_offers": 5 + }' + ``` + +
+ + If `fleets` is not set in the run configuration, offers are returned from + all configured backends. Use `"fleets": ["default"]` to restrict offers to + a fleet. + + To group offers by GPU and other fields, use the + [gpus](../reference/http/gpus.md) API. + +Creating fleets uses `/fleets/get_plan` followed by `/fleets/apply`: + +
+ +```shell +$ curl "<server URL>/api/project/<project name>/fleets/get_plan" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{ + "spec": { + "configuration": { + "type": "fleet", + "name": "cpu-fleet", + "nodes": "0..1", + "idle_duration": "1h", + "resources": { + "gpu": 0 + } + }, + "profile": {} + } + }' +``` + +
+ +Then apply the fleet plan: + +
+ +```shell +$ curl "<server URL>/api/project/<project name>/fleets/apply" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{ + "plan": { + "spec": { + "configuration": { + "type": "fleet", + "name": "cpu-fleet", + "nodes": "0..1", + "idle_duration": "1h", + "resources": { + "gpu": 0 + } + }, + "profile": {} + } + }, + "force": false + }' +``` + +
+ +### Submit runs + +Use the [runs](../reference/http/runs.md) API to submit +[dev environments](../concepts/dev-environments.md), [tasks](../concepts/tasks.md), +and [services](../concepts/services.md). The example below submits a task: + +
+ +```shell +$ curl "<server URL>/api/project/<project name>/runs/apply" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{ + "plan": { + "run_spec": { + "run_name": "hello-api", + "configuration": { + "type": "task", + "commands": ["echo hello world"] + } + } + }, + "force": false + }' +``` + +
+ +Set `run_name` if a stable run name is needed. Otherwise, the server can +generate a run name. + +Poll `/runs/get` to check the run status: + +
+ +```shell +$ curl "<server URL>/api/project/<project name>/runs/get" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{"run_name": "hello-api"}' +``` + +
+ +### Poll logs + +Use the [logs](../reference/http/logs.md) API to poll logs. Get +`job_submission_id` from `/runs/get`, e.g. from `latest_job_submission.id`. + +
+ +```shell +$ curl "<server URL>/api/project/<project name>/logs/poll" \ + -X POST \ + -H "Authorization: Bearer <user token>" \ + -H 'Content-Type: application/json' \ + -d '{ + "run_name": "hello-api", + "job_submission_id": "<job submission id>", + "limit": 100 + }' +``` + +
+ +Use `next_token` from the response to continue polling. + +## Reference + +For complete details on specific CLI commands and HTTP APIs, see the +[`dstack server`](../reference/cli/dstack/server.md) and +[server](../reference/http/server.md) references. + +!!! info "OpenAPI" + For complete information on the HTTP API, or to generate native clients, + refer to [openapi.json](../reference/http/openapi.json). + +!!! info "What's next?" + 1. Follow the [installation guide](../installation.md) + 2. Read about [projects](../concepts/projects.md) + 3. Check [fleets](../concepts/fleets.md), + [dev environments](../concepts/dev-environments.md), + [tasks](../concepts/tasks.md), and [services](../concepts/services.md) diff --git a/mkdocs/docs/installation.md b/mkdocs/docs/installation.md index 0ff4f624a8..9203f1879b 100644 --- a/mkdocs/docs/installation.md +++ b/mkdocs/docs/installation.md @@ -9,7 +9,7 @@ description: How to install the dstack server and CLI If you don't want to host the `dstack` server (or want to access GPU marketplace), skip installation and proceed to [dstack Sky](https://sky.dstack.ai). --> -## Launch the server +## Launch the server { #server } The server can run on your laptop or any environment with access to the cloud and on-prem clusters you plan to use. @@ -74,7 +74,7 @@ For more details on server deployment options, see the [Server deployment](guide To orchestrate compute across GPU clouds or Kubernetes clusters, you need to configure [backends](concepts/backends.md). -## Install the CLI +## Install the CLI { #cli } Once the server is up, you can access it via the `dstack` CLI. @@ -203,42 +203,18 @@ Later, you can create additional projects and users. Once the project is configured, you can use the `dstack` CLI or API. -## Install agent skills +> See the [CLI & API](guides/cli-api.md) guide for using the CLI and HTTP API. -Install [`dstack` skills](https://skills.sh/dstackai/dstack/dstack) to help AI agents use the CLI and edit configuration files. +## Install agent skills { #skills } -
- -```shell -$ npx skills add dstackai/dstack -``` - -
- -### Use agents - -AI agents like Claude, Codex, and Cursor can now create and manage fleets and submit workloads on your behalf. +If you'd like to use `dstack` with AI agents like Claude, Codex, and Cursor, +install [`dstack` skills](https://skills.sh/dstackai/dstack/dstack) to help +them use the CLI and edit configuration files.
```shell - ▐▛███▜▌ Claude Code v2.1.83 -▝▜█████▛▘ Opus 4.6 (1M context) · Claude Team - ▘▘ ▝▝ ~/skills-demo - -$ /dstack - -dstack skill loaded. How can I help? For example: - - - Apply a configuration (*.dstack.yml) - - Check run status (dstack ps) - - Manage fleets, volumes, or services - - Create or edit a dstack configuration - - Troubleshoot provisioning or connectivity issues - - What would you like to do? - -$ +$ npx skills add dstackai/dstack ```
diff --git a/mkdocs/docs/quickstart.md b/mkdocs/docs/quickstart.md index da37d46ded..f5de84e3ee 100644 --- a/mkdocs/docs/quickstart.md +++ b/mkdocs/docs/quickstart.md @@ -5,8 +5,8 @@ description: Quick guide to creating fleets and submitting runs # Quickstart -!!! info "Prerequsites" - Before using `dstack`, ensure you've [installed](installation.md) the server, CLI, and agent skills. +!!! info "Prerequisites" + Ensure the server and CLI are [installed](installation.md). To use `dstack` with AI agents, install [skills](installation.md#skills). ## Create a fleet diff --git a/mkdocs/docs/reference/environment-variables.md b/mkdocs/docs/reference/env.md similarity index 100% rename from mkdocs/docs/reference/environment-variables.md rename to mkdocs/docs/reference/env.md diff --git a/mkdocs/docs/reference/http/authentication.md b/mkdocs/docs/reference/http/authentication.md new file mode 100644 index 0000000000..539743146e --- /dev/null +++ b/mkdocs/docs/reference/http/authentication.md @@ -0,0 +1,5 @@ +--- +title: authentication +--- + +!!swagger openapi.json tag="authentication"!! diff --git a/mkdocs/docs/reference/http/backends.md b/mkdocs/docs/reference/http/backends.md new file mode 100644 index 0000000000..24a12894a2 --- /dev/null +++ b/mkdocs/docs/reference/http/backends.md @@ -0,0 +1,5 @@ +--- +title: backends +--- + +!!swagger openapi.json tag="backends"!! diff --git a/mkdocs/docs/reference/http/default.md b/mkdocs/docs/reference/http/default.md new file mode 100644 index 0000000000..ff45baea78 --- /dev/null +++ b/mkdocs/docs/reference/http/default.md @@ -0,0 +1,5 @@ +--- +title: default +--- + +!!swagger openapi.json tag="default"!! diff --git a/mkdocs/docs/reference/http/events.md b/mkdocs/docs/reference/http/events.md new file mode 100644 index 0000000000..ef60663097 --- /dev/null +++ b/mkdocs/docs/reference/http/events.md @@ -0,0 +1,5 @@ +--- +title: events +--- + +!!swagger openapi.json tag="events"!! diff --git a/mkdocs/docs/reference/http/exports.md b/mkdocs/docs/reference/http/exports.md new file mode 100644 index 0000000000..9859c5b2a0 --- /dev/null +++ b/mkdocs/docs/reference/http/exports.md @@ -0,0 +1,5 @@ +--- +title: exports +--- + +!!swagger openapi.json tag="exports"!! diff --git a/mkdocs/docs/reference/http/files.md b/mkdocs/docs/reference/http/files.md new file mode 100644 index 0000000000..b4c724c8c7 --- /dev/null +++ b/mkdocs/docs/reference/http/files.md @@ -0,0 +1,5 @@ +--- +title: files +--- + +!!swagger openapi.json tag="files"!! diff --git a/mkdocs/docs/reference/http/fleets.md b/mkdocs/docs/reference/http/fleets.md new file mode 100644 index 0000000000..1eb04597fc --- /dev/null +++ b/mkdocs/docs/reference/http/fleets.md @@ -0,0 +1,5 @@ +--- +title: fleets +--- + +!!swagger openapi.json tag="fleets"!! diff --git a/mkdocs/docs/reference/http/gateways.md b/mkdocs/docs/reference/http/gateways.md new file mode 100644 index 0000000000..8947d22f30 --- /dev/null +++ b/mkdocs/docs/reference/http/gateways.md @@ -0,0 +1,5 @@ +--- +title: gateways +--- + +!!swagger openapi.json tag="gateways"!! diff --git a/mkdocs/docs/reference/http/gpus.md b/mkdocs/docs/reference/http/gpus.md new file mode 100644 index 0000000000..86cc023c79 --- /dev/null +++ b/mkdocs/docs/reference/http/gpus.md @@ -0,0 +1,5 @@ +--- +title: gpus +--- + +!!swagger openapi.json tag="gpus"!! diff --git a/mkdocs/docs/reference/api/http/index.md b/mkdocs/docs/reference/http/index.md similarity index 67% rename from mkdocs/docs/reference/api/http/index.md rename to mkdocs/docs/reference/http/index.md index aa11771d90..3e3de7d75b 100644 --- a/mkdocs/docs/reference/api/http/index.md +++ b/mkdocs/docs/reference/http/index.md @@ -1,5 +1,7 @@ --- title: HTTP API +hide: + - toc --- The HTTP API enables running tasks, services, and managing runs programmatically. @@ -55,30 +57,29 @@ while True: time.sleep(2) ``` - - -

- -!!swagger openapi.json!! + +- [server](server.md) +- [users](users.md) +- [authentication](authentication.md) +- [projects](projects.md) +- [backends](backends.md) +- [fleets](fleets.md) +- [repos](repos.md) +- [runs](runs.md) +- [gpus](gpus.md) +- [metrics](metrics.md) +- [logs](logs.md) +- [secrets](secrets.md) +- [gateways](gateways.md) +- [volumes](volumes.md) +- [proxy](proxy.md) +- [files](files.md) +- [events](events.md) +- [templates](templates.md) +- [exports](exports.md) +- [default](default.md) + diff --git a/mkdocs/docs/reference/http/logs.md b/mkdocs/docs/reference/http/logs.md new file mode 100644 index 0000000000..9f5e9900bc --- /dev/null +++ b/mkdocs/docs/reference/http/logs.md @@ -0,0 +1,5 @@ +--- +title: logs +--- + +!!swagger openapi.json tag="logs"!! diff --git a/mkdocs/docs/reference/http/metrics.md b/mkdocs/docs/reference/http/metrics.md new file mode 100644 index 0000000000..88b3b1da7a --- /dev/null +++ b/mkdocs/docs/reference/http/metrics.md @@ -0,0 +1,5 @@ +--- +title: metrics +--- + +!!swagger openapi.json tag="metrics"!! diff --git a/mkdocs/docs/reference/http/projects.md b/mkdocs/docs/reference/http/projects.md new file mode 100644 index 0000000000..814a98f1c0 --- /dev/null +++ b/mkdocs/docs/reference/http/projects.md @@ -0,0 +1,5 @@ +--- +title: projects +--- + +!!swagger openapi.json tag="projects"!! diff --git a/mkdocs/docs/reference/http/proxy.md b/mkdocs/docs/reference/http/proxy.md new file mode 100644 index 0000000000..3dc5cf57f7 --- /dev/null +++ b/mkdocs/docs/reference/http/proxy.md @@ -0,0 +1,5 @@ +--- +title: proxy +--- + +!!swagger openapi.json tag="proxy"!! diff --git a/mkdocs/docs/reference/http/repos.md b/mkdocs/docs/reference/http/repos.md new file mode 100644 index 0000000000..ab050bea6a --- /dev/null +++ b/mkdocs/docs/reference/http/repos.md @@ -0,0 +1,5 @@ +--- +title: repos +--- + +!!swagger openapi.json tag="repos"!! diff --git a/mkdocs/docs/reference/http/runs.md b/mkdocs/docs/reference/http/runs.md new file mode 100644 index 0000000000..18be3b87ed --- /dev/null +++ b/mkdocs/docs/reference/http/runs.md @@ -0,0 +1,5 @@ +--- +title: runs +--- + +!!swagger openapi.json tag="runs"!! diff --git a/mkdocs/docs/reference/http/secrets.md b/mkdocs/docs/reference/http/secrets.md new file mode 100644 index 0000000000..20e8284898 --- /dev/null +++ b/mkdocs/docs/reference/http/secrets.md @@ -0,0 +1,5 @@ +--- +title: secrets +--- + +!!swagger openapi.json tag="secrets"!! diff --git a/mkdocs/docs/reference/http/server.md b/mkdocs/docs/reference/http/server.md new file mode 100644 index 0000000000..cf30aefb31 --- /dev/null +++ b/mkdocs/docs/reference/http/server.md @@ -0,0 +1,5 @@ +--- +title: server +--- + +!!swagger openapi.json tag="server"!! diff --git a/mkdocs/docs/reference/http/templates.md b/mkdocs/docs/reference/http/templates.md new file mode 100644 index 0000000000..98b2185c7d --- /dev/null +++ b/mkdocs/docs/reference/http/templates.md @@ -0,0 +1,5 @@ +--- +title: templates +--- + +!!swagger openapi.json tag="templates"!! diff --git a/mkdocs/docs/reference/http/users.md b/mkdocs/docs/reference/http/users.md new file mode 100644 index 0000000000..7c526ce7d2 --- /dev/null +++ b/mkdocs/docs/reference/http/users.md @@ -0,0 +1,5 @@ +--- +title: users +--- + +!!swagger openapi.json tag="users"!! diff --git a/mkdocs/docs/reference/http/volumes.md b/mkdocs/docs/reference/http/volumes.md new file mode 100644 index 0000000000..e0f5dd15ba --- /dev/null +++ b/mkdocs/docs/reference/http/volumes.md @@ -0,0 +1,5 @@ +--- +title: volumes +--- + +!!swagger openapi.json tag="volumes"!! diff --git a/mkdocs/overrides/home.html b/mkdocs/overrides/home.html index 34762eb7d5..20bb304ed2 100644 --- a/mkdocs/overrides/home.html +++ b/mkdocs/overrides/home.html @@ -540,7 +540,7 @@

Bring your existing GPU clusters

-

Run development environments

+

Run dev environments

If you or your agent need a development environment with a GPU, let dstack create you a dev environment.

diff --git a/mkdocs/overrides/main.html b/mkdocs/overrides/main.html index 342e29303b..0e56191e19 100644 --- a/mkdocs/overrides/main.html +++ b/mkdocs/overrides/main.html @@ -146,7 +146,7 @@ {% endblock %} {% block scripts %} - + {{ super() }}