@scalar/api-client 3.3.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/monacoeditorwork/yaml.worker.bundle.js +92 -79
- package/dist/style.css +87 -57
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts +2 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +5 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.d.ts +4 -0
- package/dist/v2/blocks/operation-block/components/Header.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.js +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js +6 -0
- package/dist/v2/blocks/operation-block/components/Header.vue.script.js.map +1 -1
- package/dist/v2/blocks/operation-block/helpers/response-cache.d.ts +2 -1
- package/dist/v2/blocks/operation-block/helpers/response-cache.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/helpers/response-cache.js +9 -2
- package/dist/v2/blocks/operation-block/helpers/response-cache.js.map +1 -1
- package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/request-block/components/RequestTable.vue.d.ts +2 -2
- package/dist/v2/blocks/request-block/components/RequestTableRow.vue.d.ts +2 -2
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts +4 -0
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.d.ts.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js +84 -71
- package/dist/v2/blocks/scalar-address-bar-block/components/AddressBar.vue.script.js.map +1 -1
- package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.d.ts +13 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.d.ts.map +1 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.js +28 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/is-placeholder-path.js.map +1 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.d.ts +16 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.d.ts.map +1 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.js +28 -0
- package/dist/v2/blocks/scalar-address-bar-block/helpers/refocus-blur-target.js.map +1 -0
- package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.d.ts +31 -0
- package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.d.ts.map +1 -0
- package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.js +18 -0
- package/dist/v2/blocks/scalar-address-bar-block/hooks/use-path-masking.js.map +1 -0
- package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.js +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.js.map +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.script.js +1 -1
- package/dist/v2/components/code-input/CodeInput.vue.script.js.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/app/App.vue.d.ts +25 -1
- package/dist/v2/features/app/App.vue.d.ts.map +1 -1
- package/dist/v2/features/app/App.vue.js.map +1 -1
- package/dist/v2/features/app/App.vue.script.js +54 -39
- package/dist/v2/features/app/App.vue.script.js.map +1 -1
- package/dist/v2/features/app/app-events.js +4 -4
- package/dist/v2/features/app/app-events.js.map +1 -1
- package/dist/v2/features/app/app-state.d.ts +20 -14
- package/dist/v2/features/app/app-state.d.ts.map +1 -1
- package/dist/v2/features/app/app-state.js +89 -55
- package/dist/v2/features/app/app-state.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.d.ts +26 -3
- package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.script.js +15 -6
- package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts +2 -2
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.script.js +86 -108
- package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/CreateVersionModal.vue.d.ts +28 -0
- package/dist/v2/features/app/components/CreateVersionModal.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/CreateVersionModal.vue.js +7 -0
- package/dist/v2/features/app/components/CreateVersionModal.vue.js.map +1 -0
- package/dist/v2/features/app/components/CreateVersionModal.vue.script.js +84 -0
- package/dist/v2/features/app/components/CreateVersionModal.vue.script.js.map +1 -0
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts +26 -0
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js +9 -0
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js.map +1 -0
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js +376 -0
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js.map +1 -0
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts +16 -0
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js +7 -0
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js.map +1 -0
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js +51 -0
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js.map +1 -0
- package/dist/v2/features/app/components/SidebarDocument.vue.d.ts +45 -0
- package/dist/v2/features/app/components/SidebarDocument.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/SidebarDocument.vue.js +7 -0
- package/dist/v2/features/app/components/SidebarDocument.vue.js.map +1 -0
- package/dist/v2/features/app/components/SidebarDocument.vue.script.js +137 -0
- package/dist/v2/features/app/components/SidebarDocument.vue.script.js.map +1 -0
- package/dist/v2/features/app/helpers/check-version-conflict.d.ts +51 -0
- package/dist/v2/features/app/helpers/check-version-conflict.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/check-version-conflict.js +79 -0
- package/dist/v2/features/app/helpers/check-version-conflict.js.map +1 -0
- package/dist/v2/features/app/helpers/compute-version-status.d.ts +45 -0
- package/dist/v2/features/app/helpers/compute-version-status.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/compute-version-status.js +18 -0
- package/dist/v2/features/app/helpers/compute-version-status.js.map +1 -0
- package/dist/v2/features/app/helpers/create-draft-registry-document.d.ts +39 -0
- package/dist/v2/features/app/helpers/create-draft-registry-document.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/create-draft-registry-document.js +64 -0
- package/dist/v2/features/app/helpers/create-draft-registry-document.js.map +1 -0
- package/dist/v2/features/app/helpers/create-temp-operation.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/create-temp-operation.js +5 -8
- package/dist/v2/features/app/helpers/create-temp-operation.js.map +1 -1
- package/dist/v2/features/app/helpers/detect-document-conflicts.d.ts +26 -0
- package/dist/v2/features/app/helpers/detect-document-conflicts.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/detect-document-conflicts.js +27 -0
- package/dist/v2/features/app/helpers/detect-document-conflicts.js.map +1 -0
- package/dist/v2/features/app/helpers/filter-workspaces.d.ts +14 -14
- package/dist/v2/features/app/helpers/filter-workspaces.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/filter-workspaces.js +15 -15
- package/dist/v2/features/app/helpers/filter-workspaces.js.map +1 -1
- package/dist/v2/features/app/helpers/group-workspaces.d.ts +23 -3
- package/dist/v2/features/app/helpers/group-workspaces.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/group-workspaces.js +22 -7
- package/dist/v2/features/app/helpers/group-workspaces.js.map +1 -1
- package/dist/v2/features/app/helpers/load-registry-document.d.ts +16 -1
- package/dist/v2/features/app/helpers/load-registry-document.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/load-registry-document.js +7 -6
- package/dist/v2/features/app/helpers/load-registry-document.js.map +1 -1
- package/dist/v2/features/app/helpers/routes.d.ts +5 -1
- package/dist/v2/features/app/helpers/routes.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/routes.js +1 -1
- package/dist/v2/features/app/helpers/routes.js.map +1 -1
- package/dist/v2/features/app/helpers/version-status-presentation.d.ts +24 -0
- package/dist/v2/features/app/helpers/version-status-presentation.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/version-status-presentation.js +43 -0
- package/dist/v2/features/app/helpers/version-status-presentation.js.map +1 -0
- package/dist/v2/features/app/hooks/use-active-document-version.d.ts +41 -0
- package/dist/v2/features/app/hooks/use-active-document-version.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-active-document-version.js +60 -0
- package/dist/v2/features/app/hooks/use-active-document-version.js.map +1 -0
- package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts +71 -23
- package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts.map +1 -1
- package/dist/v2/features/app/hooks/use-sidebar-documents.js +167 -45
- package/dist/v2/features/app/hooks/use-sidebar-documents.js.map +1 -1
- package/dist/v2/features/app/hooks/use-version-conflict-check.d.ts +35 -0
- package/dist/v2/features/app/hooks/use-version-conflict-check.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-version-conflict-check.js +62 -0
- package/dist/v2/features/app/hooks/use-version-conflict-check.js.map +1 -0
- package/dist/v2/features/collection/DocumentCollection.vue.d.ts.map +1 -1
- package/dist/v2/features/collection/DocumentCollection.vue.js.map +1 -1
- package/dist/v2/features/collection/DocumentCollection.vue.script.js +6 -1
- package/dist/v2/features/collection/DocumentCollection.vue.script.js.map +1 -1
- package/dist/v2/features/collection/OperationCollection.vue.script.js +1 -0
- package/dist/v2/features/collection/OperationCollection.vue.script.js.map +1 -1
- package/dist/v2/features/collection/WorkspaceCollection.vue.script.js +1 -0
- package/dist/v2/features/collection/WorkspaceCollection.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Authentication.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Authentication.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Cookies.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Cookies.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Editor/Editor.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Editor/Editor.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Environment.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Environment.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/GetStarted.vue.d.ts +12 -4
- package/dist/v2/features/collection/components/GetStarted.vue.d.ts.map +1 -1
- package/dist/v2/features/collection/components/GetStarted.vue.js.map +1 -1
- package/dist/v2/features/collection/components/GetStarted.vue.script.js +56 -13
- package/dist/v2/features/collection/components/GetStarted.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Overview.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Overview.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Runner/components/Runner.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Runner/components/Runner.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Runner/hooks/use-runner-execution.js +2 -2
- package/dist/v2/features/collection/components/Runner/hooks/use-runner-execution.js.map +1 -1
- package/dist/v2/features/collection/components/Scripts.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Scripts.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Servers.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Servers.vue.script.js.map +1 -1
- package/dist/v2/features/collection/components/Settings.vue.script.js +1 -0
- package/dist/v2/features/collection/components/Settings.vue.script.js.map +1 -1
- package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js +1 -1
- package/dist/v2/features/operation/Operation.vue.d.ts.map +1 -1
- package/dist/v2/features/operation/Operation.vue.js.map +1 -1
- package/dist/v2/features/operation/Operation.vue.script.js +3 -0
- package/dist/v2/features/operation/Operation.vue.script.js.map +1 -1
- package/dist/v2/helpers/safe-run.d.ts +25 -1
- package/dist/v2/helpers/safe-run.d.ts.map +1 -1
- package/dist/v2/helpers/safe-run.js +26 -2
- package/dist/v2/helpers/safe-run.js.map +1 -1
- package/package.json +12 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppHeader.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/AppHeader.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AppHeader.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/AppHeader.vue"],"names":[],"mappings":"AA4GA,KAAK,WAAW,GAAG;IACjB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAC;AASF,KAAK,WAAW,GAAG;IACjB;;;;OAIG;IACH,IAAI,CAAC,IAAI,OAAO,CAAA;IAChB,0CAA0C;IAC1C,SAAS,CAAC,IAAI,OAAO,CAAA;IACrB;;;;;OAKG;IACH,UAAU,CAAC,IAAI,OAAO,CAAA;IACtB,iDAAiD;IACjD,GAAG,CAAC,IAAI,OAAO,CAAA;CAChB,CAAC;AA2KF,QAAA,MAAM,UAAU;;;;kFAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppHeader.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppHeader.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarHeader,\n ScalarMenu,\n ScalarMenuLink,\n ScalarMenuProducts,\n ScalarMenuResources,\n ScalarMenuSection,\n} from '@scalar/components'\nimport { ScalarIconGear } from '@scalar/icons'\n\nconst emit = defineEmits<{\n /** Emitted when the user wants to open the workspace settings */\n (e: 'navigate:to:settings'): void\n}>()\n\nconst slots = defineSlots<{\n /** Slot for customizing the menu items */\n menuItems?(): unknown\n /** Slot for customizing the end of the header */\n end?(): unknown\n}>()\n</script>\n\n<template>\n <ScalarHeader class=\"w-full pl-3 *:first:flex-none\">\n <template #start>\n <ScalarMenu>\n <template #products>\n <ScalarMenuProducts selected=\"client\" />\n </template>\n <template #sections=\"{ close }\">\n <ScalarMenuSection>\n <slot name=\"menuItems\"
|
|
1
|
+
{"version":3,"file":"AppHeader.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppHeader.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarHeader,\n ScalarMenu,\n ScalarMenuLink,\n ScalarMenuProducts,\n ScalarMenuResources,\n ScalarMenuSection,\n} from '@scalar/components'\nimport { ScalarIconGear } from '@scalar/icons'\n\ndefineProps<{\n /**\n * Inline label rendered inside the menu trigger between the logo and the\n * caret. Used to surface the active scope (\"Team\" / \"Local\") so the menu\n * trigger doubles as the leading breadcrumb segment. Pass a plain string\n * so consumers do not have to pierce through a slot pipeline just to\n * tell the header which workspace type is active.\n */\n menuTitle?: string\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the user wants to open the workspace settings */\n (e: 'navigate:to:settings'): void\n}>()\n\nconst slots = defineSlots<{\n /**\n * Replaces the Scalar logo rendered inside the menu button. Typically used\n * by team-aware consumers to surface a team avatar or workspace logo so the\n * header reads as \"this team / this workspace\" at a glance.\n */\n logo?(): unknown\n /** Slot for customizing the menu items */\n menuItems?(): unknown\n /**\n * Slot rendered directly after the menu button in the start section of\n * the header. Typically used for a document breadcrumb / version-picker\n * combination that sits alongside the menu rather than floating in the\n * middle of the header.\n */\n breadcrumb?(): unknown\n /** Slot for customizing the end of the header */\n end?(): unknown\n}>()\n</script>\n\n<template>\n <ScalarHeader class=\"w-full pl-3 *:first:flex-none\">\n <template #start>\n <ScalarMenu>\n <template\n v-if=\"slots.logo\"\n #logo>\n <slot name=\"logo\" />\n </template>\n <template\n v-if=\"menuTitle\"\n #title>\n {{ menuTitle }}\n </template>\n <template #products>\n <ScalarMenuProducts selected=\"client\" />\n </template>\n <template #sections=\"{ close }\">\n <ScalarMenuSection>\n <slot name=\"menuItems\">\n <ScalarMenuLink\n is=\"button\"\n :icon=\"ScalarIconGear\"\n @click=\"\n () => {\n close()\n emit('navigate:to:settings')\n }\n \">\n Settings\n </ScalarMenuLink>\n </slot>\n </ScalarMenuSection>\n <ScalarMenuResources />\n </template>\n </ScalarMenu>\n <slot\n v-if=\"slots.breadcrumb\"\n name=\"breadcrumb\" />\n </template>\n <template\n v-if=\"slots.end\"\n #end>\n <slots.end />\n </template>\n </ScalarHeader>\n</template>\n"],"mappings":""}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { createBlock, createSlots, createTextVNode, createVNode, defineComponent, openBlock, renderSlot, unref, useSlots, withCtx } from "vue";
|
|
1
|
+
import { createBlock, createCommentVNode, createSlots, createTextVNode, createVNode, defineComponent, openBlock, renderSlot, toDisplayString, unref, useSlots, withCtx } from "vue";
|
|
2
2
|
import { ScalarHeader, ScalarMenu, ScalarMenuLink, ScalarMenuProducts, ScalarMenuResources, ScalarMenuSection } from "@scalar/components";
|
|
3
3
|
import { ScalarIconGear } from "@scalar/icons";
|
|
4
4
|
//#region src/v2/features/app/components/AppHeader.vue?vue&type=script&setup=true&lang.ts
|
|
5
5
|
var AppHeader_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
6
6
|
__name: "AppHeader",
|
|
7
|
+
props: { menuTitle: {} },
|
|
7
8
|
emits: ["navigate:to:settings"],
|
|
8
9
|
setup(__props, { emit: __emit }) {
|
|
9
10
|
const emit = __emit;
|
|
10
11
|
const slots = useSlots();
|
|
11
12
|
return (_ctx, _cache) => {
|
|
12
13
|
return openBlock(), createBlock(unref(ScalarHeader), { class: "w-full pl-3 *:first:flex-none" }, createSlots({
|
|
13
|
-
start: withCtx(() => [createVNode(unref(ScalarMenu), null, {
|
|
14
|
+
start: withCtx(() => [createVNode(unref(ScalarMenu), null, createSlots({
|
|
14
15
|
products: withCtx(() => [createVNode(unref(ScalarMenuProducts), { selected: "client" })]),
|
|
15
16
|
sections: withCtx(({ close }) => [createVNode(unref(ScalarMenuSection), null, {
|
|
16
|
-
default: withCtx(() => [renderSlot(_ctx.$slots, "menuItems"
|
|
17
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "menuItems", {}, () => [createVNode(unref(ScalarMenuLink), {
|
|
17
18
|
is: "button",
|
|
18
19
|
icon: unref(ScalarIconGear),
|
|
19
20
|
onClick: () => {
|
|
@@ -23,11 +24,19 @@ var AppHeader_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defi
|
|
|
23
24
|
}, {
|
|
24
25
|
default: withCtx(() => [..._cache[0] || (_cache[0] = [createTextVNode(" Settings ", -1)])]),
|
|
25
26
|
_: 1
|
|
26
|
-
}, 8, ["icon", "onClick"])]),
|
|
27
|
+
}, 8, ["icon", "onClick"])])]),
|
|
27
28
|
_: 2
|
|
28
29
|
}, 1024), createVNode(unref(ScalarMenuResources))]),
|
|
29
|
-
_:
|
|
30
|
-
}
|
|
30
|
+
_: 2
|
|
31
|
+
}, [slots.logo ? {
|
|
32
|
+
name: "logo",
|
|
33
|
+
fn: withCtx(() => [renderSlot(_ctx.$slots, "logo")]),
|
|
34
|
+
key: "0"
|
|
35
|
+
} : void 0, __props.menuTitle ? {
|
|
36
|
+
name: "title",
|
|
37
|
+
fn: withCtx(() => [createTextVNode(toDisplayString(__props.menuTitle), 1)]),
|
|
38
|
+
key: "1"
|
|
39
|
+
} : void 0]), 1024), slots.breadcrumb ? renderSlot(_ctx.$slots, "breadcrumb", { key: 0 }) : createCommentVNode("", true)]),
|
|
31
40
|
_: 2
|
|
32
41
|
}, [slots.end ? {
|
|
33
42
|
name: "end",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppHeader.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppHeader.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarHeader,\n ScalarMenu,\n ScalarMenuLink,\n ScalarMenuProducts,\n ScalarMenuResources,\n ScalarMenuSection,\n} from '@scalar/components'\nimport { ScalarIconGear } from '@scalar/icons'\n\nconst emit = defineEmits<{\n /** Emitted when the user wants to open the workspace settings */\n (e: 'navigate:to:settings'): void\n}>()\n\nconst slots = defineSlots<{\n /** Slot for customizing the menu items */\n menuItems?(): unknown\n /** Slot for customizing the end of the header */\n end?(): unknown\n}>()\n</script>\n\n<template>\n <ScalarHeader class=\"w-full pl-3 *:first:flex-none\">\n <template #start>\n <ScalarMenu>\n <template #products>\n <ScalarMenuProducts selected=\"client\" />\n </template>\n <template #sections=\"{ close }\">\n <ScalarMenuSection>\n <slot name=\"menuItems\"
|
|
1
|
+
{"version":3,"file":"AppHeader.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppHeader.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarHeader,\n ScalarMenu,\n ScalarMenuLink,\n ScalarMenuProducts,\n ScalarMenuResources,\n ScalarMenuSection,\n} from '@scalar/components'\nimport { ScalarIconGear } from '@scalar/icons'\n\ndefineProps<{\n /**\n * Inline label rendered inside the menu trigger between the logo and the\n * caret. Used to surface the active scope (\"Team\" / \"Local\") so the menu\n * trigger doubles as the leading breadcrumb segment. Pass a plain string\n * so consumers do not have to pierce through a slot pipeline just to\n * tell the header which workspace type is active.\n */\n menuTitle?: string\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the user wants to open the workspace settings */\n (e: 'navigate:to:settings'): void\n}>()\n\nconst slots = defineSlots<{\n /**\n * Replaces the Scalar logo rendered inside the menu button. Typically used\n * by team-aware consumers to surface a team avatar or workspace logo so the\n * header reads as \"this team / this workspace\" at a glance.\n */\n logo?(): unknown\n /** Slot for customizing the menu items */\n menuItems?(): unknown\n /**\n * Slot rendered directly after the menu button in the start section of\n * the header. Typically used for a document breadcrumb / version-picker\n * combination that sits alongside the menu rather than floating in the\n * middle of the header.\n */\n breadcrumb?(): unknown\n /** Slot for customizing the end of the header */\n end?(): unknown\n}>()\n</script>\n\n<template>\n <ScalarHeader class=\"w-full pl-3 *:first:flex-none\">\n <template #start>\n <ScalarMenu>\n <template\n v-if=\"slots.logo\"\n #logo>\n <slot name=\"logo\" />\n </template>\n <template\n v-if=\"menuTitle\"\n #title>\n {{ menuTitle }}\n </template>\n <template #products>\n <ScalarMenuProducts selected=\"client\" />\n </template>\n <template #sections=\"{ close }\">\n <ScalarMenuSection>\n <slot name=\"menuItems\">\n <ScalarMenuLink\n is=\"button\"\n :icon=\"ScalarIconGear\"\n @click=\"\n () => {\n close()\n emit('navigate:to:settings')\n }\n \">\n Settings\n </ScalarMenuLink>\n </slot>\n </ScalarMenuSection>\n <ScalarMenuResources />\n </template>\n </ScalarMenu>\n <slot\n v-if=\"slots.breadcrumb\"\n name=\"breadcrumb\" />\n </template>\n <template\n v-if=\"slots.end\"\n #end>\n <slots.end />\n </template>\n </ScalarHeader>\n</template>\n"],"mappings":";;;;;;;;;EAsBA,MAAM,OAAO;EAKb,MAAM,QAAQ,UAAA;;uBAsBZ,YA4Ce,MAAA,aAAA,EAAA,EA5CD,OAAM,iCAA+B,EAAA,YAAA;IACtC,OAAK,cAiCD,CAhCb,YAgCa,MAAA,WAAA,EAAA,MAAA,YAAA;KArBA,UAAQ,cACuB,CAAxC,YAAwC,MAAA,mBAAA,EAAA,EAApB,UAAS,UAAQ,CAAA,CAAA,CAAA;KAE5B,UAAQ,SAeG,EAfC,YAAK,CAC1B,YAcoB,MAAA,kBAAA,EAAA,MAAA;6BADX,CAZP,WAYO,KAAA,QAAA,aAAA,EAAA,QAAA,CAXL,YAUiB,MAAA,eAAA,EAAA;OATf,IAAG;OACF,MAAM,MAAA,eAAc;OACpB,eAAA;AAAsD,eAAK;AAAuB,aAAI,uBAAA;;;8BAOzF,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAFI,cAEJ,GAAA,CAAA,EAAA,CAAA;;;;eAGJ,YAAuB,MAAA,oBAAA,CAAA,CAAA,CAAA;;QA5BjB,MAAM,OAAA;WACX;uBACmB,CAApB,WAAoB,KAAA,QAAA,OAAA,CAAA,CAAA;;gBAGd,QAAA,YAAA;WACL;uBACc,CAAA,gBAAA,gBAAZ,QAAA,UAAS,EAAA,EAAA,CAAA,CAAA;;yBAyBR,MAAM,aADd,WAEsB,KAAA,QAAA,cAAA,EAAA,KAAA,GAAA,CAAA,GAAA,mBAAA,IAAA,KAAA,CAAA,CAAA;;OAGhB,MAAM,MAAA;UACX;sBACY,CAAb,YAAa,MAAA,IAAA,CAAA,CAAA"}
|
|
@@ -20,9 +20,9 @@ type __VLS_ModelProps = {
|
|
|
20
20
|
'sidebarWidth': number;
|
|
21
21
|
};
|
|
22
22
|
type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
|
|
23
|
-
declare var
|
|
23
|
+
declare var __VLS_106: {};
|
|
24
24
|
type __VLS_Slots = {} & {
|
|
25
|
-
footer?: (props: typeof
|
|
25
|
+
footer?: (props: typeof __VLS_106) => any;
|
|
26
26
|
};
|
|
27
27
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
28
28
|
"update:sidebarWidth": (value: number) => any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSidebar.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AppSidebar.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"names":[],"mappings":"AA2mBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AA+BlE,OAAO,EAEL,KAAK,sBAAsB,EAE5B,MAAM,+CAA+C,CAAA;AAItD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,KAAK,WAAW,GAAG;IACjB,6CAA6C;IAC7C,GAAG,EAAE,QAAQ,CAAA;IACb,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;IAC1C,8CAA8C;IAC9C,qBAAqB,CAAC,EAAE,0BAA0B,CAAA;CACnD,CAAC;AAkXF,KAAK,gBAAgB,GAAG;IACxB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;CACtB,CAAC;AAKF,KAAK,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,CAAC;AA2oBxD,QAAA,IAAI,SAAS,IAAa,CAAE;AAC5B,KAAK,WAAW,GAAG,EAAE,GACnB;IAAE,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,SAAS,KAAK,GAAG,CAAA;CAAE,CAAC;AAKhD,QAAA,MAAM,UAAU;;;;kFAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -2,7 +2,7 @@ import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_
|
|
|
2
2
|
import AppSidebar_vue_vue_type_script_setup_true_lang_default from "./AppSidebar.vue.script.js";
|
|
3
3
|
/* empty css */
|
|
4
4
|
//#region src/v2/features/app/components/AppSidebar.vue
|
|
5
|
-
var AppSidebar_default = /* @__PURE__ */ _plugin_vue_export_helper_default(AppSidebar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
5
|
+
var AppSidebar_default = /* @__PURE__ */ _plugin_vue_export_helper_default(AppSidebar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-1181f741"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { AppSidebar_default as default };
|
|
8
8
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppSidebar.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { AppState } from '@scalar/api-client/v2/features/app'\nimport {\n ScalarIconButton,\n ScalarModal,\n ScalarSidebar,\n ScalarSidebarButton,\n ScalarSidebarItem,\n ScalarSidebarItems,\n ScalarSidebarNestedItems,\n ScalarSidebarSearchInput,\n ScalarSidebarSection,\n useModal,\n} from '@scalar/components'\nimport {\n ScalarIconCaretLeft,\n ScalarIconDotsThree,\n ScalarIconFolderDashed,\n ScalarIconFunnel,\n ScalarIconGearSix,\n ScalarIconMagnifyingGlass,\n ScalarIconPlus,\n} from '@scalar/icons'\nimport {\n filterItems,\n SidebarItem,\n type DraggingItem,\n type HoveredItem,\n} from '@scalar/sidebar'\nimport { useToasts } from '@scalar/use-toasts'\nimport { getParentEntry } from '@scalar/workspace-store/navigation'\nimport type { TraversedEntry } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'\n\nimport DeleteSidebarListElement from '@/components/Sidebar/Actions/DeleteSidebarListElement.vue'\nimport { Resize } from '@/v2/components/resize'\nimport SidebarItemMenu from '@/v2/features/app/components/SidebarItemMenu.vue'\nimport { createTempOperation } from '@/v2/features/app/helpers/create-temp-operation'\nimport { loadRegistryDocument } from '@/v2/features/app/helpers/load-registry-document'\nimport { useDocumentFilter } from '@/v2/features/app/hooks/use-document-filter'\nimport { useSidebarContextMenu } from '@/v2/features/app/hooks/use-sidebar-context-menu'\nimport {\n useSidebarDocuments,\n type RegistryDocumentsState,\n type SidebarDocumentItem,\n} from '@/v2/features/app/hooks/use-sidebar-documents'\nimport { DocumentSearchModal } from '@/v2/features/search'\nimport { dragHandleFactory } from '@/v2/helpers/drag-handle-factory'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nconst {\n app,\n indent = 20,\n registryDocuments = { status: 'success', documents: [] },\n fetchRegistryDocument,\n} = defineProps<{\n /** The app state from @scalar/api-client. */\n app: AppState\n /** Horizontal indent applied to nested sidebar items, in pixels. */\n indent?: number\n /**\n * The list of all available registry documents, wrapped in a loading state\n * so the sidebar can render skeleton placeholders while the registry is\n * still being fetched.\n */\n registryDocuments?: RegistryDocumentsState\n /** A function to fetch a registry document */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n}>()\n\nconst { toast } = useToasts()\n\n/**\n * Whether the caller is still fetching the list of registry documents. We\n * only surface the loading state on team workspaces because local workspaces\n * never consult the registry\n */\nconst isLoadingRegistry = computed(\n () =>\n registryDocuments.status === 'loading' &&\n app.workspace.isTeamWorkspace.value,\n)\n\nconst { rest } = useSidebarDocuments({\n app,\n managedDocs: () => registryDocuments.documents ?? [],\n})\n\n/**\n * Whether the workspace truly has no documents to show. Distinct from the\n * filter producing no results: we only surface the \"No APIs yet\" empty state\n * when the workspace is genuinely empty so users see a clear call-to-action\n * instead of a confusing blank space.\n */\nconst isEmpty = computed(\n () => !isLoadingRegistry.value && rest.value.length === 0,\n)\n\n/**\n * Fuzzy filter over the top-level documents. Owns its own input visibility,\n * query string and Fuse index. See `use-document-filter.ts` for details.\n */\nconst {\n isVisible: isFilterVisible,\n query: filterQuery,\n filteredItems: filteredRest,\n toggle: toggleFilter,\n} = useDocumentFilter(rest)\n\nconst sidebarState = app.sidebar.state\n\n/** Which registry documents are currently being fetched. */\nconst loadingKeys = ref<Record<string, boolean>>({})\n\n/**\n * Check if the given {@link SidebarDocumentItem} is the currently active document (from the sidebar state).\n */\nconst isDocActive = (item: SidebarDocumentItem) => {\n if (!item.navigation) {\n return false\n }\n\n return (\n sidebarState.selectedItem.value === item.navigation.id ||\n Boolean(sidebarState.selectedItems.value[item.navigation.id])\n )\n}\n\nconst handleDocumentClick = async (item: SidebarDocumentItem) => {\n if (item.navigation) {\n app.sidebar.handleSelectItem(item.navigation.id)\n return\n }\n\n if (!item.registry || !app.store.value) {\n console.warn('Document does not have a sidebar navigation, skipping...')\n return\n }\n\n if (!fetchRegistryDocument) {\n console.warn(\n 'You need to provide a fetchRegistryDocument function to load registry documents',\n )\n return\n }\n\n if (loadingKeys.value[item.key]) {\n return\n }\n\n loadingKeys.value[item.key] = true\n\n const result = await loadRegistryDocument({\n fetcher: fetchRegistryDocument,\n workspaceStore: app.store.value,\n namespace: item.registry.namespace,\n slug: item.registry.slug,\n })\n\n loadingKeys.value[item.key] = false\n\n if (!result.ok) {\n toast(result.error, 'error')\n return\n }\n\n // After loading, route to the document overview. `syncSidebar` will then\n // mark the document as selected and the template's `:open=\"isDocActive\"`\n // binding drills the sidebar in automatically — no local state needed.\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'overview',\n documentSlug: result.documentName,\n })\n}\n\nconst isSelected = (id: string) => sidebarState.isSelected(id)\nconst isExpanded = (id: string) => sidebarState.isExpanded(id)\n\nconst handleSelectItem = (id: string) => {\n app.sidebar.handleSelectItem(id)\n}\n\nconst handleToggleGroup = (id: string) => {\n sidebarState.setExpanded(id, !sidebarState.isExpanded(id))\n}\n\n/**\n * Drag-and-drop handlers for sidebar items. The factory reads from the live\n * workspace store and shared sidebar state, so it reflects the latest\n * navigation tree on every drag. Mirrors the behaviour of the old\n * `AppSidebar.vue` (documents, tags, and operations can be reordered, and\n * operations can be moved between tags/documents).\n */\nconst dragHandlers = computed(() =>\n dragHandleFactory({\n store: app.store,\n sidebarState,\n }),\n)\n\nconst handleDragEnd = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => dragHandlers.value.handleDragEnd(draggingItem, hoveredItem)\n\nconst isDroppable = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => dragHandlers.value.isDroppable(draggingItem, hoveredItem)\n\n/**\n * Contextual \"more\" dropdown for tags, operations and examples, together\n * with the shared delete-confirmation modal it triggers.\n */\nconst {\n menuTarget,\n deleteModalState,\n deleteMessage,\n openMenu,\n closeMenu,\n handleDelete,\n} = useSidebarContextMenu({\n eventBus: app.eventBus,\n sidebarState,\n})\n\n/**\n * Create a new operation from an empty folder slot inside a tag or document.\n * If the entry is a tag, the new operation inherits that tag so it stays\n * grouped under the same folder.\n */\nconst handleAddEmptyFolder = (item: TraversedEntry) => {\n const itemWithParent = sidebarState.getEntryById(item.id)\n const documentName = getParentEntry('document', itemWithParent)?.name\n const tagName = getParentEntry('tag', itemWithParent)?.name\n const store = app.store.value\n\n if (!documentName || !store) {\n console.error('Document name not found')\n return\n }\n\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus: app.eventBus,\n tags: tagName ? [tagName] : undefined,\n })\n}\n\nconst handleCreate = () => {\n app.eventBus.emit('ui:open:command-palette', {\n action: 'create-openapi-document',\n payload: undefined,\n })\n}\n\n/**\n * Create a new operation inside the given document and immediately navigate to\n * it. Uses `createTempOperation` so the operation gets a unique `/temp…` path,\n * then the sidebar focuses the new example and the address bar is focused so\n * the user can start typing the real path right away.\n */\nconst handleCreateOperation = (item: SidebarDocumentItem) => {\n const documentName = item.documentName\n const store = app.store.value\n\n if (!documentName || !store) {\n console.warn('Cannot create an operation: no document loaded')\n return\n }\n\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus: app.eventBus,\n })\n}\n\n/**\n * Navigates back to the workspace \"Get started\" page.\n */\nconst handleBack = () => {\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'get-started',\n })\n}\n\n/**\n * True when the user is currently viewing a document (any of its subpages).\n * When inside a document, the sidebar actions are scoped to that document:\n * the gear icon opens the document (collection) settings and the filter icon\n * becomes a search icon that focuses the search input.\n */\nconst isOnDocumentPage = computed(() =>\n Boolean(app.activeEntities.documentSlug.value),\n)\n\nconst handleOpenSettings = () => {\n if (isOnDocumentPage.value) {\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'settings',\n documentSlug: app.activeEntities.documentSlug.value,\n })\n return\n }\n\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'settings',\n })\n}\n\n/**\n * Controls the per-document search modal. Only used when the user is drilled\n * into a single document and clicks the magnifying-glass icon.\n */\nconst searchModal = useModal()\n\n/**\n * The OpenAPI document currently selected in the workspace. The search modal\n * scopes its Fuse index to this document so results never leak across\n * collections.\n */\nconst activeDocument = computed(() => app.store.value?.workspace.activeDocument)\n\nconst handleFilterOrSearch = () => {\n // Inside a document, this icon opens a modal search that is scoped to that\n // single document (similar to the reference search modal), so users can jump\n // to any operation / tag / heading without noise from other documents.\n if (isOnDocumentPage.value) {\n searchModal.show()\n return\n }\n\n // At the top-level documents view, the icon toggles a lightweight filter\n // that narrows the visible documents by title.\n toggleFilter()\n}\n\n/**\n * Handle the `ui:focus:search` event. Dispatch is driven by the shared\n * `handleHotkeys` helper (Cmd/Ctrl+J) or by programmatic callers such as the\n * workspace \"Get started\" page. When a `KeyboardEvent` is included we\n * preventDefault to override the browser's Cmd+J (downloads panel), then\n * delegate to `handleFilterOrSearch`, which already branches on whether the\n * user is viewing a document or the workspace root.\n */\nconst handleSearchHotkey = (payload: { event: KeyboardEvent } | undefined) => {\n payload?.event.preventDefault()\n handleFilterOrSearch()\n}\n\n/**\n * Handle the `ui:open:settings` event (Cmd/Ctrl+I). Same delegation model as\n * `handleSearchHotkey`: preventDefault on the originating keyboard event (if\n * any) and hand off to `handleOpenSettings`, which routes to the document-\n * level settings page when a document is active and the workspace-level\n * settings page otherwise.\n */\nconst handleSettingsHotkey = (\n payload: { event: KeyboardEvent } | undefined,\n) => {\n payload?.event.preventDefault()\n handleOpenSettings()\n}\n\nonBeforeMount(() => {\n app.eventBus.on('ui:focus:search', handleSearchHotkey)\n app.eventBus.on('ui:open:settings', handleSettingsHotkey)\n})\nonBeforeUnmount(() => {\n app.eventBus.off('ui:focus:search', handleSearchHotkey)\n app.eventBus.off('ui:open:settings', handleSettingsHotkey)\n})\n\n/**\n * Navigate to the selected search result. `scroll-to:nav-item` is already\n * wired up through the app event bus to update the sidebar + route, matching\n * the behaviour used elsewhere in the app.\n */\nconst handleSearchSelect = (id: string) => {\n app.eventBus.emit('scroll-to:nav-item', { id })\n}\n\n/** Controls the width of the sidebar */\nconst sidebarWidth = defineModel<number>('sidebarWidth', {\n required: true,\n default: 288,\n})\n</script>\n\n<template>\n <Resize\n v-model:width=\"sidebarWidth\"\n class=\"flex flex-col max-md:inset-y-0 max-md:z-2 max-md:w-full!\"\n :class=\"{\n 'max-md:absolute! max-md:flex!': app.sidebar.isOpen.value,\n 'max-md:hidden!': !app.sidebar.isOpen.value,\n }\">\n <template #default>\n <div class=\"flex flex-1\">\n <ScalarSidebar\n class=\"flex min-h-0 flex-1 flex-col max-md:pt-12\"\n :style=\"{ '--scalar-sidebar-indent': indent + 'px' }\">\n <!-- Top-level sidebar header -->\n <div\n v-if=\"!isOnDocumentPage\"\n class=\"flex flex-col gap-1.5 p-(--scalar-sidebar-padding)\">\n <div class=\"flex items-center gap-1\">\n <ScalarSidebarButton\n is=\"div\"\n class=\"text-sidebar-c-1 font-sidebar-active flex-1\"\n disabled>\n All Documents\n </ScalarSidebarButton>\n <ScalarIconButton\n :icon=\"ScalarIconGearSix\"\n label=\"Workspace settings\"\n size=\"sm\"\n @click=\"handleOpenSettings\" />\n <ScalarIconButton\n :icon=\"ScalarIconFunnel\"\n label=\"Filter documents\"\n size=\"sm\"\n @click=\"handleFilterOrSearch\" />\n <ScalarIconButton\n class=\"rounded-full border\"\n :icon=\"ScalarIconPlus\"\n label=\"Add document\"\n size=\"sm\"\n @click=\"handleCreate\" />\n </div>\n <ScalarSidebarSearchInput\n v-if=\"isFilterVisible\"\n v-model=\"filterQuery\"\n autofocus />\n </div>\n\n <!-- Document list (top-level) -->\n <div class=\"custom-scroll flex flex-1 flex-col\">\n <!--\n Empty state: no documents in the workspace yet. Matches the\n minimal `empty folder` appearance.\n -->\n <div\n v-if=\"isEmpty && !isOnDocumentPage\"\n class=\"text-c-3 flex flex-1 flex-col items-center justify-center gap-2 p-6 text-center select-none\">\n <ScalarIconFolderDashed\n class=\"size-10\"\n weight=\"light\" />\n <p class=\"text-sm font-medium\">No APIs yet</p>\n </div>\n <ScalarSidebarItems v-else>\n <!-- Show pinned documents after we add support for it -->\n <!-- <ScalarSidebarSection v-if=\"pinned.length\">\n Pinned\n <template #items>\n <ScalarSidebarNestedItems\n v-for=\"item in pinned\"\n :key=\"item.key\"\n :active=\"isDocActive(item)\"\n controlled\n :open=\"Boolean(openKeys[item.key])\"\n @back=\"openKeys[item.key] = false\"\n @click=\"handleDocumentClick(item)\">\n <span class=\"truncate\">{{ item.title }}</span>\n <template #back-label>{{ item.title }}</template>\n <template #items>\n <SidebarItem\n v-for=\"child in filterItems(item.navigation?.children ?? [])\"\n :key=\"child.id\"\n :isDraggable=\"false\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"child\"\n layout=\"client\"\n @selectItem=\"handleSelectItem\"\n @toggleGroup=\"handleToggleGroup\" />\n </template>\n </ScalarSidebarNestedItems>\n </template>\n </ScalarSidebarSection> -->\n\n <ScalarSidebarSection>\n All documents\n <template #items>\n <!--\n Skeleton rows shown while the caller is still fetching\n the registry document list. We only render skeletons in\n the top-level view (when no document is drilled-in) so\n the collection view is never masked by placeholders.\n -->\n <template v-if=\"isLoadingRegistry && !isOnDocumentPage\">\n <li\n v-for=\"n in 4\"\n :key=\"`registry-skeleton-${n}`\"\n aria-hidden=\"true\"\n class=\"sidebar-skeleton-row px-(--scalar-sidebar-padding) py-1\">\n <span class=\"bg-b-3 block h-6 rounded-md\" />\n </li>\n </template>\n <ScalarSidebarNestedItems\n v-for=\"item in filteredRest\"\n :key=\"item.key\"\n :active=\"isDocActive(item)\"\n controlled\n :open=\"isDocActive(item)\"\n @back=\"handleBack\"\n @click=\"handleDocumentClick(item)\">\n <span>{{ item.title }}</span>\n <template\n v-if=\"loadingKeys[item.key]\"\n #aside>\n <span class=\"text-c-3 text-xs\">Loading…</span>\n </template>\n <!-- Document back row -->\n <template #back>\n <div class=\"flex items-center gap-1\">\n <ScalarSidebarButton\n is=\"button\"\n class=\"text-sidebar-c-1 font-sidebar-active hover:text-sidebar-c-1 flex-1\"\n @click=\"handleBack\">\n <template #icon>\n <ScalarIconCaretLeft\n class=\"text-sidebar-c-2 -m-px size-4\" />\n </template>\n Back\n </ScalarSidebarButton>\n <ScalarIconButton\n :icon=\"ScalarIconGearSix\"\n label=\"Collection settings\"\n size=\"sm\"\n @click=\"handleOpenSettings\" />\n <ScalarIconButton\n :icon=\"ScalarIconMagnifyingGlass\"\n label=\"Search collection\"\n size=\"sm\"\n @click=\"handleFilterOrSearch\" />\n <ScalarIconButton\n class=\"rounded-full border\"\n :icon=\"ScalarIconPlus\"\n label=\"Add operation\"\n size=\"sm\"\n @click=\"handleCreateOperation(item)\" />\n </div>\n </template>\n <!-- Document items (operations, tags, examples) -->\n <template #items>\n <template v-if=\"item.navigation?.children?.length\">\n <SidebarItem\n v-for=\"child in filterItems(\n 'client',\n item.navigation.children,\n )\"\n :key=\"child.id\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"child\"\n layout=\"client\"\n @onDragEnd=\"handleDragEnd\"\n @selectItem=\"handleSelectItem\"\n @toggleGroup=\"handleToggleGroup\">\n <!--\n Per-item \"more\" dropdown for tags, operations and\n examples (add / edit / delete…). The dropdown is\n rendered once at the sidebar root and anchors\n itself to whichever icon button opened it.\n Operation settings live on the operation header\n (next to the environment selector), not here.\n -->\n <template #decorator=\"{ item: entry }\">\n <ScalarIconButton\n aria-expanded=\"false\"\n aria-haspopup=\"menu\"\n class=\"bg-b-2\"\n :icon=\"ScalarIconDotsThree\"\n label=\"More options\"\n size=\"sm\"\n variant=\"ghost\"\n weight=\"bold\"\n @click.stop=\"\n (e: MouseEvent) => openMenu(e, entry)\n \"\n @keydown.down.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \"\n @keydown.enter.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \"\n @keydown.space.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \"\n @keydown.up.stop=\"\n (e: KeyboardEvent) => openMenu(e, entry)\n \" />\n </template>\n <!--\n Empty tag / folder slot. Matches the \"Add operation\"\n affordance from the old sidebar so users can create\n an operation directly inside the hovered tag.\n -->\n <template #empty=\"{ item: emptyItem }\">\n <ScalarSidebarItem\n is=\"button\"\n @click=\"handleAddEmptyFolder(emptyItem)\">\n <template #icon>\n <ScalarIconPlus />\n </template>\n <template #default>Add operation</template>\n </ScalarSidebarItem>\n </template>\n </SidebarItem>\n </template>\n <li\n v-else\n class=\"text-c-3 px-3 py-1 text-xs\">\n Empty document\n </li>\n </template>\n </ScalarSidebarNestedItems>\n </template>\n </ScalarSidebarSection>\n </ScalarSidebarItems>\n </div>\n\n <slot name=\"footer\" />\n </ScalarSidebar>\n </div>\n <DocumentSearchModal\n :document=\"activeDocument\"\n :modalState=\"searchModal\"\n @select=\"handleSearchSelect\" />\n <!--\n Contextual dropdown menu for tags, operations and examples. Rendered\n once for the whole sidebar and re-anchored to the triggering icon via\n `menuTarget.el`, so we do not create a dropdown per item.\n -->\n <SidebarItemMenu\n v-if=\"app.store.value && menuTarget?.showMenu\"\n :eventBus=\"app.eventBus\"\n :item=\"menuTarget.item\"\n :sidebarState=\"sidebarState\"\n :target=\"menuTarget.el\"\n :workspaceStore=\"app.store.value\"\n @closeMenu=\"closeMenu\"\n @showDeleteModal=\"deleteModalState.show()\" />\n <!-- Delete confirmation modal, triggered from the dropdown menu above. -->\n <ScalarModal\n v-if=\"menuTarget\"\n size=\"xxs\"\n :state=\"deleteModalState\"\n :title=\"`Delete ${menuTarget.item.title}`\">\n <DeleteSidebarListElement\n :variableName=\"menuTarget.item.title\"\n :warningMessage=\"deleteMessage\"\n @close=\"deleteModalState.hide()\"\n @delete=\"handleDelete\" />\n </ScalarModal>\n </template>\n </Resize>\n</template>\n\n<style scoped>\n/*\n * Gentle pulse for the registry loading skeletons. Matches the existing\n * `LoadingSkeleton.vue` easing/duration used in `@scalar/api-reference` so\n * any skeleton in the app feels consistent.\n */\n.sidebar-skeleton-row > span {\n animation: sidebar-skeleton-pulse 1.5s infinite alternate;\n}\n\n@keyframes sidebar-skeleton-pulse {\n from {\n opacity: 1;\n }\n to {\n opacity: 0.33;\n }\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"AppSidebar.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { AppState } from '@scalar/api-client/v2/features/app'\nimport {\n ScalarIconButton,\n ScalarModal,\n ScalarSidebar,\n ScalarSidebarButton,\n ScalarSidebarItems,\n ScalarSidebarSearchInput,\n ScalarSidebarSection,\n useModal,\n} from '@scalar/components'\nimport {\n ScalarIconFolderDashed,\n ScalarIconFunnel,\n ScalarIconGearSix,\n ScalarIconPlus,\n} from '@scalar/icons'\nimport type { DraggingItem, HoveredItem } from '@scalar/sidebar'\nimport { useToasts } from '@scalar/use-toasts'\nimport { getParentEntry } from '@scalar/workspace-store/navigation'\nimport type { TraversedEntry } from '@scalar/workspace-store/schemas/navigation'\nimport { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'\n\nimport DeleteSidebarListElement from '@/components/Sidebar/Actions/DeleteSidebarListElement.vue'\nimport { Resize } from '@/v2/components/resize'\nimport SidebarDocument from '@/v2/features/app/components/SidebarDocument.vue'\nimport SidebarItemMenu from '@/v2/features/app/components/SidebarItemMenu.vue'\nimport { createTempOperation } from '@/v2/features/app/helpers/create-temp-operation'\nimport { loadRegistryDocument } from '@/v2/features/app/helpers/load-registry-document'\nimport { useDocumentFilter } from '@/v2/features/app/hooks/use-document-filter'\nimport { useSidebarContextMenu } from '@/v2/features/app/hooks/use-sidebar-context-menu'\nimport {\n useSidebarDocuments,\n type RegistryDocumentsState,\n type SidebarDocumentItem,\n} from '@/v2/features/app/hooks/use-sidebar-documents'\nimport { DocumentSearchModal } from '@/v2/features/search'\nimport { dragHandleFactory } from '@/v2/helpers/drag-handle-factory'\nimport { safeRun } from '@/v2/helpers/safe-run'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nconst {\n app,\n indent = 20,\n registryDocuments = { status: 'success', documents: [] },\n fetchRegistryDocument,\n} = defineProps<{\n /** The app state from @scalar/api-client. */\n app: AppState\n /** Horizontal indent applied to nested sidebar items, in pixels. */\n indent?: number\n /**\n * The list of all available registry documents, wrapped in a loading state\n * so the sidebar can render skeleton placeholders while the registry is\n * still being fetched.\n */\n registryDocuments?: RegistryDocumentsState\n /** A function to fetch a registry document */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n}>()\n\nconst { toast } = useToasts()\n\n/**\n * Whether the caller is still fetching the list of registry documents. We\n * only surface the loading state on team workspaces because local workspaces\n * never consult the registry\n */\nconst isLoadingRegistry = computed(\n () =>\n registryDocuments.status === 'loading' &&\n app.workspace.isTeamWorkspace.value,\n)\n\nconst { pinned, rest } = useSidebarDocuments({\n app,\n managedDocs: () => registryDocuments.documents ?? [],\n})\n\n/**\n * Whether the workspace truly has no documents to show. Distinct from the\n * filter producing no results: we only surface the \"No APIs yet\" empty state\n * when the workspace is genuinely empty so users see a clear call-to-action\n * instead of a confusing blank space.\n */\nconst isEmpty = computed(\n () => !isLoadingRegistry.value && rest.value.length === 0,\n)\n\n/**\n * Fuzzy filter over the top-level documents. Owns its own input visibility,\n * query string and Fuse index. See `use-document-filter.ts` for details.\n */\nconst {\n isVisible: isFilterVisible,\n query: filterQuery,\n filteredItems: filteredRest,\n toggle: toggleFilter,\n} = useDocumentFilter(rest)\n\nconst sidebarState = app.sidebar.state\n\n/** Which registry documents are currently being fetched. */\nconst loadingKeys = ref<Record<string, boolean>>({})\n\n/**\n * Check if the given {@link SidebarDocumentItem} is the currently active document (from the sidebar state).\n */\nconst isDocActive = (item: SidebarDocumentItem) => {\n if (!item.navigation) {\n return false\n }\n\n return (\n sidebarState.selectedItem.value === item.navigation.id ||\n Boolean(sidebarState.selectedItems.value[item.navigation.id])\n )\n}\n\nconst handleDocumentClick = async (item: SidebarDocumentItem) => {\n if (item.navigation) {\n app.sidebar.handleSelectItem(item.navigation.id)\n return\n }\n\n // Capture the narrowed values into locals so the closure passed to\n // `safeRun` keeps the non-nullable types without needing assertions, and\n // so a later mutation to `item.registry` or `app.store.value` cannot\n // change what we end up loading mid-flight.\n const { registry } = item\n const workspaceStore = app.store.value\n if (!registry || !workspaceStore) {\n console.warn('Document does not have a sidebar navigation, skipping...')\n return\n }\n\n if (!fetchRegistryDocument) {\n console.warn(\n 'You need to provide a fetchRegistryDocument function to load registry documents',\n )\n return\n }\n\n if (loadingKeys.value[item.key]) {\n return\n }\n\n loadingKeys.value[item.key] = true\n\n // Registry items expose every version they advertise on `versions`, ordered\n // with the latest first. Until we surface a version picker on the parent\n // row we always load whichever version is at the top of the list, which is\n // the latest one. Falling back to `undefined` lets the loader default to\n // the registry's `latest` alias when the entry was synthesized from a\n // workspace document that has no advertised versions yet.\n const targetVersion = item.versions?.[0]\n\n // The loader can throw on network errors or malformed payloads. `safeRun`\n // converts a rejection into an `{ ok: false, error }` result so a single\n // failure cannot leave the row's spinner running forever and block\n // subsequent clicks on the same item.\n const outcome = await safeRun(() =>\n loadRegistryDocument({\n fetcher: fetchRegistryDocument,\n workspaceStore,\n namespace: registry.namespace,\n slug: registry.slug,\n version: targetVersion?.version,\n // Forward the registry-advertised hash from the version row. Storing it\n // on the imported document lets us later detect when the registry has\n // moved on and surface upstream changes.\n commitHash: targetVersion?.registryCommitHash,\n }),\n )\n\n loadingKeys.value[item.key] = false\n\n if (!outcome.ok) {\n toast(outcome.error, 'error')\n return\n }\n\n const result = outcome.data\n if (!result.ok) {\n toast(result.error, 'error')\n return\n }\n\n // After loading, route to the document overview. `syncSidebar` will then\n // mark the document as selected and the template's `:open=\"isDocActive\"`\n // binding drills the sidebar in automatically — no local state needed.\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'overview',\n documentSlug: result.documentName,\n })\n}\n\nconst isSelected = (id: string) => sidebarState.isSelected(id)\nconst isExpanded = (id: string) => sidebarState.isExpanded(id)\n\nconst handleSelectItem = (id: string) => {\n app.sidebar.handleSelectItem(id)\n}\n\nconst handleToggleGroup = (id: string) => {\n sidebarState.setExpanded(id, !sidebarState.isExpanded(id))\n}\n\n/**\n * Drag-and-drop handlers for sidebar items. The factory reads from the live\n * workspace store and shared sidebar state, so it reflects the latest\n * navigation tree on every drag. Mirrors the behaviour of the old\n * `AppSidebar.vue` (documents, tags, and operations can be reordered, and\n * operations can be moved between tags/documents).\n */\nconst dragHandlers = computed(() =>\n dragHandleFactory({\n store: app.store,\n sidebarState,\n }),\n)\n\nconst handleDragEnd = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => dragHandlers.value.handleDragEnd(draggingItem, hoveredItem)\n\nconst isDroppable = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => dragHandlers.value.isDroppable(draggingItem, hoveredItem)\n\n/**\n * Contextual \"more\" dropdown for tags, operations and examples, together\n * with the shared delete-confirmation modal it triggers.\n */\nconst {\n menuTarget,\n deleteModalState,\n deleteMessage,\n openMenu,\n closeMenu,\n handleDelete,\n} = useSidebarContextMenu({\n eventBus: app.eventBus,\n sidebarState,\n})\n\n/**\n * Create a new operation from an empty folder slot inside a tag or document.\n * If the entry is a tag, the new operation inherits that tag so it stays\n * grouped under the same folder.\n */\nconst handleAddEmptyFolder = (item: TraversedEntry) => {\n const itemWithParent = sidebarState.getEntryById(item.id)\n const documentName = getParentEntry('document', itemWithParent)?.name\n const tagName = getParentEntry('tag', itemWithParent)?.name\n const store = app.store.value\n\n if (!documentName || !store) {\n console.error('Document name not found')\n return\n }\n\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus: app.eventBus,\n tags: tagName ? [tagName] : undefined,\n })\n}\n\nconst handleCreate = () => {\n app.eventBus.emit('ui:open:command-palette', {\n action: 'create-openapi-document',\n payload: undefined,\n })\n}\n\n/**\n * Create a new operation inside the given document and immediately navigate to\n * it. Uses `createTempOperation` so the operation gets a unique `/temp…` path,\n * then the sidebar focuses the new example and the address bar is focused so\n * the user can start typing the real path right away.\n */\nconst handleCreateOperation = (item: SidebarDocumentItem) => {\n const documentName = item.documentName\n const store = app.store.value\n\n if (!documentName || !store) {\n console.warn('Cannot create an operation: no document loaded')\n return\n }\n\n createTempOperation(documentName, {\n existingPaths: new Set(\n Object.keys(store.workspace.documents[documentName]?.paths ?? {}),\n ),\n eventBus: app.eventBus,\n })\n}\n\n/**\n * Navigates back to the workspace \"Get started\" page.\n */\nconst handleBack = () => {\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'get-started',\n })\n}\n\n/**\n * True when the user is currently viewing a document (any of its subpages).\n * When inside a document, the sidebar actions are scoped to that document:\n * the gear icon opens the document (collection) settings and the filter icon\n * becomes a search icon that focuses the search input.\n */\nconst isOnDocumentPage = computed(() =>\n Boolean(app.activeEntities.documentSlug.value),\n)\n\nconst handleOpenSettings = () => {\n if (isOnDocumentPage.value) {\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'settings',\n documentSlug: app.activeEntities.documentSlug.value,\n })\n return\n }\n\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'settings',\n })\n}\n\n/**\n * Controls the per-document search modal. Only used when the user is drilled\n * into a single document and clicks the magnifying-glass icon.\n */\nconst searchModal = useModal()\n\n/**\n * The OpenAPI document currently selected in the workspace. The search modal\n * scopes its Fuse index to this document so results never leak across\n * collections.\n */\nconst activeDocument = computed(() => app.store.value?.workspace.activeDocument)\n\nconst handleFilterOrSearch = () => {\n // Inside a document, this icon opens a modal search that is scoped to that\n // single document (similar to the reference search modal), so users can jump\n // to any operation / tag / heading without noise from other documents.\n if (isOnDocumentPage.value) {\n searchModal.show()\n return\n }\n\n // At the top-level documents view, the icon toggles a lightweight filter\n // that narrows the visible documents by title.\n toggleFilter()\n}\n\n/**\n * Handle the `ui:focus:search` event. Dispatch is driven by the shared\n * `handleHotkeys` helper (Cmd/Ctrl+J) or by programmatic callers such as the\n * workspace \"Get started\" page. When a `KeyboardEvent` is included we\n * preventDefault to override the browser's Cmd+J (downloads panel), then\n * delegate to `handleFilterOrSearch`, which already branches on whether the\n * user is viewing a document or the workspace root.\n */\nconst handleSearchHotkey = (payload: { event: KeyboardEvent } | undefined) => {\n payload?.event.preventDefault()\n handleFilterOrSearch()\n}\n\n/**\n * Handle the `ui:open:settings` event (Cmd/Ctrl+I). Same delegation model as\n * `handleSearchHotkey`: preventDefault on the originating keyboard event (if\n * any) and hand off to `handleOpenSettings`, which routes to the document-\n * level settings page when a document is active and the workspace-level\n * settings page otherwise.\n */\nconst handleSettingsHotkey = (\n payload: { event: KeyboardEvent } | undefined,\n) => {\n payload?.event.preventDefault()\n handleOpenSettings()\n}\n\nonBeforeMount(() => {\n app.eventBus.on('ui:focus:search', handleSearchHotkey)\n app.eventBus.on('ui:open:settings', handleSettingsHotkey)\n})\nonBeforeUnmount(() => {\n app.eventBus.off('ui:focus:search', handleSearchHotkey)\n app.eventBus.off('ui:open:settings', handleSettingsHotkey)\n})\n\n/**\n * Navigate to the selected search result. `scroll-to:nav-item` is already\n * wired up through the app event bus to update the sidebar + route, matching\n * the behaviour used elsewhere in the app.\n */\nconst handleSearchSelect = (id: string) => {\n app.eventBus.emit('scroll-to:nav-item', { id })\n}\n\n/** Controls the width of the sidebar */\nconst sidebarWidth = defineModel<number>('sidebarWidth', {\n required: true,\n default: 288,\n})\n</script>\n\n<template>\n <Resize\n v-model:width=\"sidebarWidth\"\n class=\"flex flex-col max-md:inset-y-0 max-md:z-2 max-md:w-full!\"\n :class=\"{\n 'max-md:absolute! max-md:flex!': app.sidebar.isOpen.value,\n 'max-md:hidden!': !app.sidebar.isOpen.value,\n }\">\n <template #default>\n <div class=\"flex flex-1\">\n <ScalarSidebar\n class=\"flex min-h-0 flex-1 flex-col max-md:pt-12\"\n :style=\"{ '--scalar-sidebar-indent': indent + 'px' }\">\n <!-- Top-level sidebar header -->\n <div\n v-if=\"!isOnDocumentPage\"\n class=\"flex flex-col gap-1.5 p-(--scalar-sidebar-padding)\">\n <div class=\"flex items-center gap-1\">\n <ScalarSidebarButton\n is=\"div\"\n class=\"text-sidebar-c-1 font-sidebar-active flex-1\"\n disabled>\n All Documents\n </ScalarSidebarButton>\n <ScalarIconButton\n :icon=\"ScalarIconGearSix\"\n label=\"Workspace settings\"\n size=\"sm\"\n @click=\"handleOpenSettings\" />\n <ScalarIconButton\n :icon=\"ScalarIconFunnel\"\n label=\"Filter documents\"\n size=\"sm\"\n @click=\"handleFilterOrSearch\" />\n <ScalarIconButton\n class=\"rounded-full border\"\n :icon=\"ScalarIconPlus\"\n label=\"Add document\"\n size=\"sm\"\n @click=\"handleCreate\" />\n </div>\n <ScalarSidebarSearchInput\n v-if=\"isFilterVisible\"\n v-model=\"filterQuery\"\n autofocus />\n </div>\n\n <!-- Document list (top-level) -->\n <div class=\"custom-scroll flex flex-1 flex-col\">\n <!--\n Empty state: no documents in the workspace yet. Matches the\n minimal `empty folder` appearance.\n -->\n <div\n v-if=\"isEmpty && !isOnDocumentPage\"\n class=\"text-c-3 flex flex-1 flex-col items-center justify-center gap-2 p-6 text-center select-none\">\n <ScalarIconFolderDashed\n class=\"size-10\"\n weight=\"light\" />\n <p class=\"text-sm font-medium\">Nothing added yet</p>\n </div>\n <ScalarSidebarItems v-else>\n <!-- Show pinned documents after we add support for it -->\n <ScalarSidebarSection v-if=\"pinned.length\">\n <template\n v-if=\"pinned.length && rest.length\"\n #default>\n Pinned\n </template>\n <template #items>\n <SidebarDocument\n v-for=\"item in pinned\"\n :key=\"item.key\"\n :active=\"isDocActive(item)\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"item\"\n :loading=\"loadingKeys[item.key]\"\n :open=\"isDocActive(item)\"\n @addEmptyFolder=\"handleAddEmptyFolder\"\n @back=\"handleBack\"\n @click=\"handleDocumentClick(item)\"\n @createOperation=\"handleCreateOperation\"\n @dragEnd=\"handleDragEnd\"\n @openMenu=\"openMenu\"\n @openSettings=\"handleOpenSettings\"\n @search=\"handleFilterOrSearch\"\n @selectItem=\"handleSelectItem\"\n @toggleGroup=\"handleToggleGroup\" />\n </template>\n </ScalarSidebarSection>\n\n <ScalarSidebarSection>\n <template\n v-if=\"pinned.length && rest.length\"\n #default>\n All documents\n </template>\n <template #items>\n <!--\n Skeleton rows shown while the caller is still fetching\n the registry document list. We only render skeletons in\n the top-level view (when no document is drilled-in) so\n the collection view is never masked by placeholders.\n -->\n <template v-if=\"isLoadingRegistry && !isOnDocumentPage\">\n <li\n v-for=\"n in 4\"\n :key=\"`registry-skeleton-${n}`\"\n aria-hidden=\"true\"\n class=\"sidebar-skeleton-row px-(--scalar-sidebar-padding) py-1\">\n <span class=\"bg-b-3 block h-6 rounded-md\" />\n </li>\n </template>\n <SidebarDocument\n v-for=\"item in filteredRest\"\n :key=\"item.key\"\n :active=\"isDocActive(item)\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"item\"\n :loading=\"loadingKeys[item.key]\"\n :open=\"isDocActive(item)\"\n @addEmptyFolder=\"handleAddEmptyFolder\"\n @back=\"handleBack\"\n @click=\"handleDocumentClick(item)\"\n @createOperation=\"handleCreateOperation\"\n @dragEnd=\"handleDragEnd\"\n @openMenu=\"openMenu\"\n @openSettings=\"handleOpenSettings\"\n @search=\"handleFilterOrSearch\"\n @selectItem=\"handleSelectItem\"\n @toggleGroup=\"handleToggleGroup\" />\n </template>\n </ScalarSidebarSection>\n </ScalarSidebarItems>\n </div>\n\n <slot name=\"footer\" />\n </ScalarSidebar>\n </div>\n <DocumentSearchModal\n :document=\"activeDocument\"\n :modalState=\"searchModal\"\n @select=\"handleSearchSelect\" />\n <!--\n Contextual dropdown menu for tags, operations and examples. Rendered\n once for the whole sidebar and re-anchored to the triggering icon via\n `menuTarget.el`, so we do not create a dropdown per item.\n -->\n <SidebarItemMenu\n v-if=\"app.store.value && menuTarget?.showMenu\"\n :eventBus=\"app.eventBus\"\n :item=\"menuTarget.item\"\n :sidebarState=\"sidebarState\"\n :target=\"menuTarget.el\"\n :workspaceStore=\"app.store.value\"\n @closeMenu=\"closeMenu\"\n @showDeleteModal=\"deleteModalState.show()\" />\n <!-- Delete confirmation modal, triggered from the dropdown menu above. -->\n <ScalarModal\n v-if=\"menuTarget\"\n size=\"xxs\"\n :state=\"deleteModalState\"\n :title=\"`Delete ${menuTarget.item.title}`\">\n <DeleteSidebarListElement\n :variableName=\"menuTarget.item.title\"\n :warningMessage=\"deleteMessage\"\n @close=\"deleteModalState.hide()\"\n @delete=\"handleDelete\" />\n </ScalarModal>\n </template>\n </Resize>\n</template>\n\n<style scoped>\n/*\n * Gentle pulse for the registry loading skeletons. Matches the existing\n * `LoadingSkeleton.vue` easing/duration used in `@scalar/api-reference` so\n * any skeleton in the app feels consistent.\n */\n.sidebar-skeleton-row > span {\n animation: sidebar-skeleton-pulse 1.5s infinite alternate;\n}\n\n@keyframes sidebar-skeleton-pulse {\n from {\n opacity: 1;\n }\n to {\n opacity: 0.33;\n }\n}\n</style>\n"],"mappings":""}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Resize_default from "../../../components/resize/Resize.vue.js";
|
|
2
2
|
import DocumentSearchModal_default from "../../search/components/DocumentSearchModal.vue.js";
|
|
3
3
|
import DeleteSidebarListElement_default from "../../../../components/Sidebar/Actions/DeleteSidebarListElement.vue.js";
|
|
4
|
+
import SidebarDocument_default from "./SidebarDocument.vue.js";
|
|
4
5
|
import { createTempOperation } from "../helpers/create-temp-operation.js";
|
|
5
6
|
import SidebarItemMenu_default from "./SidebarItemMenu.vue.js";
|
|
6
7
|
import { loadRegistryDocument } from "../helpers/load-registry-document.js";
|
|
@@ -8,11 +9,11 @@ import { useDocumentFilter } from "../hooks/use-document-filter.js";
|
|
|
8
9
|
import { useSidebarContextMenu } from "../hooks/use-sidebar-context-menu.js";
|
|
9
10
|
import { useSidebarDocuments } from "../hooks/use-sidebar-documents.js";
|
|
10
11
|
import { dragHandleFactory } from "../../../helpers/drag-handle-factory.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { safeRun } from "../../../helpers/safe-run.js";
|
|
13
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, isRef, mergeModels, normalizeClass, normalizeStyle, onBeforeMount, onBeforeUnmount, openBlock, ref, renderList, renderSlot, unref, useModel, withCtx } from "vue";
|
|
14
|
+
import { ScalarIconButton, ScalarModal, ScalarSidebar, ScalarSidebarButton, ScalarSidebarItems, ScalarSidebarSearchInput, ScalarSidebarSection, useModal } from "@scalar/components";
|
|
15
|
+
import { ScalarIconFolderDashed, ScalarIconFunnel, ScalarIconGearSix, ScalarIconPlus } from "@scalar/icons";
|
|
14
16
|
import { useToasts } from "@scalar/use-toasts";
|
|
15
|
-
import { SidebarItem, filterItems } from "@scalar/sidebar";
|
|
16
17
|
import { getParentEntry } from "@scalar/workspace-store/navigation";
|
|
17
18
|
//#region src/v2/features/app/components/AppSidebar.vue?vue&type=script&setup=true&lang.ts
|
|
18
19
|
var _hoisted_1 = { class: "flex flex-1" };
|
|
@@ -26,11 +27,6 @@ var _hoisted_5 = {
|
|
|
26
27
|
key: 0,
|
|
27
28
|
class: "text-c-3 flex flex-1 flex-col items-center justify-center gap-2 p-6 text-center select-none"
|
|
28
29
|
};
|
|
29
|
-
var _hoisted_6 = { class: "flex items-center gap-1" };
|
|
30
|
-
var _hoisted_7 = {
|
|
31
|
-
key: 1,
|
|
32
|
-
class: "text-c-3 px-3 py-1 text-xs"
|
|
33
|
-
};
|
|
34
30
|
var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
35
31
|
__name: "AppSidebar",
|
|
36
32
|
props: /* @__PURE__ */ mergeModels({
|
|
@@ -57,7 +53,7 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
|
|
|
57
53
|
* never consult the registry
|
|
58
54
|
*/
|
|
59
55
|
const isLoadingRegistry = computed(() => __props.registryDocuments.status === "loading" && __props.app.workspace.isTeamWorkspace.value);
|
|
60
|
-
const { rest } = useSidebarDocuments({
|
|
56
|
+
const { pinned, rest } = useSidebarDocuments({
|
|
61
57
|
app: __props.app,
|
|
62
58
|
managedDocs: () => __props.registryDocuments.documents ?? []
|
|
63
59
|
});
|
|
@@ -88,7 +84,9 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
|
|
|
88
84
|
__props.app.sidebar.handleSelectItem(item.navigation.id);
|
|
89
85
|
return;
|
|
90
86
|
}
|
|
91
|
-
|
|
87
|
+
const { registry } = item;
|
|
88
|
+
const workspaceStore = __props.app.store.value;
|
|
89
|
+
if (!registry || !workspaceStore) {
|
|
92
90
|
console.warn("Document does not have a sidebar navigation, skipping...");
|
|
93
91
|
return;
|
|
94
92
|
}
|
|
@@ -98,13 +96,21 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
|
|
|
98
96
|
}
|
|
99
97
|
if (loadingKeys.value[item.key]) return;
|
|
100
98
|
loadingKeys.value[item.key] = true;
|
|
101
|
-
const
|
|
99
|
+
const targetVersion = item.versions?.[0];
|
|
100
|
+
const outcome = await safeRun(() => loadRegistryDocument({
|
|
102
101
|
fetcher: __props.fetchRegistryDocument,
|
|
103
|
-
workspaceStore
|
|
104
|
-
namespace:
|
|
105
|
-
slug:
|
|
106
|
-
|
|
102
|
+
workspaceStore,
|
|
103
|
+
namespace: registry.namespace,
|
|
104
|
+
slug: registry.slug,
|
|
105
|
+
version: targetVersion?.version,
|
|
106
|
+
commitHash: targetVersion?.registryCommitHash
|
|
107
|
+
}));
|
|
107
108
|
loadingKeys.value[item.key] = false;
|
|
109
|
+
if (!outcome.ok) {
|
|
110
|
+
toast(outcome.error, "error");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const result = outcome.data;
|
|
108
114
|
if (!result.ok) {
|
|
109
115
|
toast(result.error, "error");
|
|
110
116
|
return;
|
|
@@ -329,112 +335,84 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
|
|
|
329
335
|
createElementVNode("div", _hoisted_4, [isEmpty.value && !isOnDocumentPage.value ? (openBlock(), createElementBlock("div", _hoisted_5, [createVNode(unref(ScalarIconFolderDashed), {
|
|
330
336
|
class: "size-10",
|
|
331
337
|
weight: "light"
|
|
332
|
-
}), _cache[5] || (_cache[5] = createElementVNode("p", { class: "text-sm font-medium" }, "
|
|
333
|
-
default: withCtx(() => [
|
|
338
|
+
}), _cache[5] || (_cache[5] = createElementVNode("p", { class: "text-sm font-medium" }, "Nothing added yet", -1))])) : (openBlock(), createBlock(unref(ScalarSidebarItems), { key: 1 }, {
|
|
339
|
+
default: withCtx(() => [unref(pinned).length ? (openBlock(), createBlock(unref(ScalarSidebarSection), { key: 0 }, createSlots({
|
|
340
|
+
items: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(pinned), (item) => {
|
|
341
|
+
return openBlock(), createBlock(SidebarDocument_default, {
|
|
342
|
+
key: item.key,
|
|
343
|
+
active: isDocActive(item),
|
|
344
|
+
isDroppable,
|
|
345
|
+
isExpanded,
|
|
346
|
+
isSelected,
|
|
347
|
+
item,
|
|
348
|
+
loading: loadingKeys.value[item.key],
|
|
349
|
+
open: isDocActive(item),
|
|
350
|
+
onAddEmptyFolder: handleAddEmptyFolder,
|
|
351
|
+
onBack: handleBack,
|
|
352
|
+
onClick: ($event) => handleDocumentClick(item),
|
|
353
|
+
onCreateOperation: handleCreateOperation,
|
|
354
|
+
onDragEnd: handleDragEnd,
|
|
355
|
+
onOpenMenu: unref(openMenu),
|
|
356
|
+
onOpenSettings: handleOpenSettings,
|
|
357
|
+
onSearch: handleFilterOrSearch,
|
|
358
|
+
onSelectItem: handleSelectItem,
|
|
359
|
+
onToggleGroup: handleToggleGroup
|
|
360
|
+
}, null, 8, [
|
|
361
|
+
"active",
|
|
362
|
+
"item",
|
|
363
|
+
"loading",
|
|
364
|
+
"open",
|
|
365
|
+
"onClick",
|
|
366
|
+
"onOpenMenu"
|
|
367
|
+
]);
|
|
368
|
+
}), 128))]),
|
|
369
|
+
_: 2
|
|
370
|
+
}, [unref(pinned).length && unref(rest).length ? {
|
|
371
|
+
name: "default",
|
|
372
|
+
fn: withCtx(() => [_cache[6] || (_cache[6] = createTextVNode(" Pinned ", -1))]),
|
|
373
|
+
key: "0"
|
|
374
|
+
} : void 0]), 1024)) : createCommentVNode("", true), createVNode(unref(ScalarSidebarSection), null, createSlots({
|
|
334
375
|
items: withCtx(() => [isLoadingRegistry.value && !isOnDocumentPage.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, renderList(4, (n) => {
|
|
335
376
|
return createElementVNode("li", {
|
|
336
377
|
key: `registry-skeleton-${n}`,
|
|
337
378
|
"aria-hidden": "true",
|
|
338
379
|
class: "sidebar-skeleton-row px-(--scalar-sidebar-padding) py-1"
|
|
339
|
-
}, [..._cache[
|
|
380
|
+
}, [..._cache[8] || (_cache[8] = [createElementVNode("span", { class: "bg-b-3 block h-6 rounded-md" }, null, -1)])]);
|
|
340
381
|
}), 64)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(filteredRest), (item) => {
|
|
341
|
-
return openBlock(), createBlock(
|
|
382
|
+
return openBlock(), createBlock(SidebarDocument_default, {
|
|
342
383
|
key: item.key,
|
|
343
384
|
active: isDocActive(item),
|
|
344
|
-
|
|
385
|
+
isDroppable,
|
|
386
|
+
isExpanded,
|
|
387
|
+
isSelected,
|
|
388
|
+
item,
|
|
389
|
+
loading: loadingKeys.value[item.key],
|
|
345
390
|
open: isDocActive(item),
|
|
391
|
+
onAddEmptyFolder: handleAddEmptyFolder,
|
|
346
392
|
onBack: handleBack,
|
|
347
|
-
onClick: ($event) => handleDocumentClick(item)
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
default: withCtx(() => [_cache[8] || (_cache[8] = createTextVNode(" Back ", -1))]),
|
|
357
|
-
_: 1
|
|
358
|
-
}),
|
|
359
|
-
createVNode(unref(ScalarIconButton), {
|
|
360
|
-
icon: unref(ScalarIconGearSix),
|
|
361
|
-
label: "Collection settings",
|
|
362
|
-
size: "sm",
|
|
363
|
-
onClick: handleOpenSettings
|
|
364
|
-
}, null, 8, ["icon"]),
|
|
365
|
-
createVNode(unref(ScalarIconButton), {
|
|
366
|
-
icon: unref(ScalarIconMagnifyingGlass),
|
|
367
|
-
label: "Search collection",
|
|
368
|
-
size: "sm",
|
|
369
|
-
onClick: handleFilterOrSearch
|
|
370
|
-
}, null, 8, ["icon"]),
|
|
371
|
-
createVNode(unref(ScalarIconButton), {
|
|
372
|
-
class: "rounded-full border",
|
|
373
|
-
icon: unref(ScalarIconPlus),
|
|
374
|
-
label: "Add operation",
|
|
375
|
-
size: "sm",
|
|
376
|
-
onClick: ($event) => handleCreateOperation(item)
|
|
377
|
-
}, null, 8, ["icon", "onClick"])
|
|
378
|
-
])]),
|
|
379
|
-
items: withCtx(() => [item.navigation?.children?.length ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(unref(filterItems)("client", item.navigation.children), (child) => {
|
|
380
|
-
return openBlock(), createBlock(unref(SidebarItem), {
|
|
381
|
-
key: child.id,
|
|
382
|
-
isDroppable,
|
|
383
|
-
isExpanded,
|
|
384
|
-
isSelected,
|
|
385
|
-
item: child,
|
|
386
|
-
layout: "client",
|
|
387
|
-
onOnDragEnd: handleDragEnd,
|
|
388
|
-
onSelectItem: handleSelectItem,
|
|
389
|
-
onToggleGroup: handleToggleGroup
|
|
390
|
-
}, {
|
|
391
|
-
decorator: withCtx(({ item: entry }) => [createVNode(unref(ScalarIconButton), {
|
|
392
|
-
"aria-expanded": "false",
|
|
393
|
-
"aria-haspopup": "menu",
|
|
394
|
-
class: "bg-b-2",
|
|
395
|
-
icon: unref(ScalarIconDotsThree),
|
|
396
|
-
label: "More options",
|
|
397
|
-
size: "sm",
|
|
398
|
-
variant: "ghost",
|
|
399
|
-
weight: "bold",
|
|
400
|
-
onClick: withModifiers((e) => unref(openMenu)(e, entry), ["stop"]),
|
|
401
|
-
onKeydown: [
|
|
402
|
-
withKeys(withModifiers((e) => unref(openMenu)(e, entry), ["stop"]), ["down"]),
|
|
403
|
-
withKeys(withModifiers((e) => unref(openMenu)(e, entry), ["stop"]), ["enter"]),
|
|
404
|
-
withKeys(withModifiers((e) => unref(openMenu)(e, entry), ["stop"]), ["space"]),
|
|
405
|
-
withKeys(withModifiers((e) => unref(openMenu)(e, entry), ["stop"]), ["up"])
|
|
406
|
-
]
|
|
407
|
-
}, null, 8, [
|
|
408
|
-
"icon",
|
|
409
|
-
"onClick",
|
|
410
|
-
"onKeydown"
|
|
411
|
-
])]),
|
|
412
|
-
empty: withCtx(({ item: emptyItem }) => [createVNode(unref(ScalarSidebarItem), {
|
|
413
|
-
is: "button",
|
|
414
|
-
onClick: ($event) => handleAddEmptyFolder(emptyItem)
|
|
415
|
-
}, {
|
|
416
|
-
icon: withCtx(() => [createVNode(unref(ScalarIconPlus))]),
|
|
417
|
-
default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode("Add operation", -1)])]),
|
|
418
|
-
_: 1
|
|
419
|
-
}, 8, ["onClick"])]),
|
|
420
|
-
_: 1
|
|
421
|
-
}, 8, ["item"]);
|
|
422
|
-
}), 128)) : (openBlock(), createElementBlock("li", _hoisted_7, " Empty document "))]),
|
|
423
|
-
default: withCtx(() => [createElementVNode("span", null, toDisplayString(item.title), 1)]),
|
|
424
|
-
_: 2
|
|
425
|
-
}, [loadingKeys.value[item.key] ? {
|
|
426
|
-
name: "aside",
|
|
427
|
-
fn: withCtx(() => [_cache[7] || (_cache[7] = createElementVNode("span", { class: "text-c-3 text-xs" }, "Loading…", -1))]),
|
|
428
|
-
key: "0"
|
|
429
|
-
} : void 0]), 1032, [
|
|
393
|
+
onClick: ($event) => handleDocumentClick(item),
|
|
394
|
+
onCreateOperation: handleCreateOperation,
|
|
395
|
+
onDragEnd: handleDragEnd,
|
|
396
|
+
onOpenMenu: unref(openMenu),
|
|
397
|
+
onOpenSettings: handleOpenSettings,
|
|
398
|
+
onSearch: handleFilterOrSearch,
|
|
399
|
+
onSelectItem: handleSelectItem,
|
|
400
|
+
onToggleGroup: handleToggleGroup
|
|
401
|
+
}, null, 8, [
|
|
430
402
|
"active",
|
|
403
|
+
"item",
|
|
404
|
+
"loading",
|
|
431
405
|
"open",
|
|
432
|
-
"onClick"
|
|
406
|
+
"onClick",
|
|
407
|
+
"onOpenMenu"
|
|
433
408
|
]);
|
|
434
409
|
}), 128))]),
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
410
|
+
_: 2
|
|
411
|
+
}, [unref(pinned).length && unref(rest).length ? {
|
|
412
|
+
name: "default",
|
|
413
|
+
fn: withCtx(() => [_cache[7] || (_cache[7] = createTextVNode(" All documents ", -1))]),
|
|
414
|
+
key: "0"
|
|
415
|
+
} : void 0]), 1024)]),
|
|
438
416
|
_: 1
|
|
439
417
|
}))]),
|
|
440
418
|
renderSlot(_ctx.$slots, "footer", {}, void 0, true)
|