@scalar/api-client 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/monacoeditorwork/yaml.worker.bundle.js +92 -79
- package/dist/style.css +107 -73
- 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/components/modals/ModalClientContainer.vue.d.ts +2 -2
- package/dist/v2/components/modals/ModalClientContainer.vue.d.ts.map +1 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.js +1 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.js.map +1 -1
- package/dist/v2/components/modals/ModalClientContainer.vue.script.js +17 -24
- package/dist/v2/components/modals/ModalClientContainer.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 +17 -16
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import "../app-state.js";
|
|
2
|
+
import { loadRegistryDocument } from "../helpers/load-registry-document.js";
|
|
3
|
+
import { safeRun } from "../../../helpers/safe-run.js";
|
|
4
|
+
import { createDraftRegistryDocument } from "../helpers/create-draft-registry-document.js";
|
|
5
|
+
import { VERSION_STATUS_PRESENTATION } from "../helpers/version-status-presentation.js";
|
|
6
|
+
import { useActiveDocumentVersion } from "../hooks/use-active-document-version.js";
|
|
7
|
+
import { useVersionConflictCheck } from "../hooks/use-version-conflict-check.js";
|
|
8
|
+
import CreateVersionModal_default from "./CreateVersionModal.vue.js";
|
|
9
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, openBlock, ref, resolveDynamicComponent, toDisplayString, unref, withCtx } from "vue";
|
|
10
|
+
import { ScalarCombobox, useModal } from "@scalar/components";
|
|
11
|
+
import { ScalarIconCaretDown } from "@scalar/icons";
|
|
12
|
+
import { useToasts } from "@scalar/use-toasts";
|
|
13
|
+
import "@scalar/workspace-store/persistence";
|
|
14
|
+
//#region src/v2/features/app/components/DocumentBreadcrumb.vue?vue&type=script&setup=true&lang.ts
|
|
15
|
+
var _hoisted_1 = {
|
|
16
|
+
key: 0,
|
|
17
|
+
"aria-label": "Document breadcrumb",
|
|
18
|
+
class: "flex min-w-0 items-center gap-2 text-sm font-medium"
|
|
19
|
+
};
|
|
20
|
+
var _hoisted_2 = {
|
|
21
|
+
"aria-label": "Workspace",
|
|
22
|
+
class: "hover:bg-b-2 flex min-w-0 items-center gap-1 rounded px-1.5 py-0.5",
|
|
23
|
+
type: "button"
|
|
24
|
+
};
|
|
25
|
+
var _hoisted_3 = { class: "truncate" };
|
|
26
|
+
var _hoisted_4 = ["disabled"];
|
|
27
|
+
var _hoisted_5 = {
|
|
28
|
+
key: 0,
|
|
29
|
+
class: "text-c-3 ml-2 shrink-0 text-xs"
|
|
30
|
+
};
|
|
31
|
+
var _hoisted_6 = {
|
|
32
|
+
key: 0,
|
|
33
|
+
class: "text-c-3 ml-1 text-xs"
|
|
34
|
+
};
|
|
35
|
+
var DocumentBreadcrumb_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
36
|
+
__name: "DocumentBreadcrumb",
|
|
37
|
+
props: {
|
|
38
|
+
app: {},
|
|
39
|
+
registryDocuments: { default: () => ({
|
|
40
|
+
status: "success",
|
|
41
|
+
documents: []
|
|
42
|
+
}) },
|
|
43
|
+
fetchRegistryDocument: { type: Function }
|
|
44
|
+
},
|
|
45
|
+
emits: ["createWorkspace"],
|
|
46
|
+
setup(__props, { emit: __emit }) {
|
|
47
|
+
const emit = __emit;
|
|
48
|
+
const { toast } = useToasts();
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the active document group, its versions, and the currently
|
|
51
|
+
* selected version once. The right-side sync indicator consumes the same
|
|
52
|
+
* composable so the two surfaces always agree on what "active" means.
|
|
53
|
+
*/
|
|
54
|
+
const { activeRegistryMeta, activeItem, versions, activeVersion } = useActiveDocumentVersion({
|
|
55
|
+
app: __props.app,
|
|
56
|
+
registryDocuments: () => __props.registryDocuments
|
|
57
|
+
});
|
|
58
|
+
/** Workspace label rendered as the first segment of the breadcrumb. */
|
|
59
|
+
const workspaceTitle = computed(() => __props.app.workspace.activeWorkspace.value?.label ?? "");
|
|
60
|
+
/** Identifier for the active workspace - used as the combobox selection. */
|
|
61
|
+
const activeWorkspaceId = computed(() => __props.app.workspace.activeWorkspace.value?.id);
|
|
62
|
+
/**
|
|
63
|
+
* Workspaces grouped by team, mapped into the shape `ScalarCombobox`
|
|
64
|
+
* expects. We always render this as a grouped combobox - even when there
|
|
65
|
+
* is only one group - so the dropdown layout stays consistent and the
|
|
66
|
+
* group label clearly separates `Team Workspaces` from `Local Workspaces`.
|
|
67
|
+
*/
|
|
68
|
+
const workspaceGroups = computed(() => __props.app.workspace.workspaceGroups.value.map((group) => ({
|
|
69
|
+
label: group.label ?? "",
|
|
70
|
+
options: group.options.map((option) => ({
|
|
71
|
+
id: option.id,
|
|
72
|
+
label: option.label
|
|
73
|
+
}))
|
|
74
|
+
})));
|
|
75
|
+
/**
|
|
76
|
+
* Currently selected option object. The combobox compares by reference, so
|
|
77
|
+
* we have to look the option up inside `workspaceGroups` rather than build
|
|
78
|
+
* a fresh `{ id, label }` here.
|
|
79
|
+
*/
|
|
80
|
+
const selectedWorkspaceOption = computed(() => {
|
|
81
|
+
const id = activeWorkspaceId.value;
|
|
82
|
+
if (!id) return;
|
|
83
|
+
for (const group of workspaceGroups.value) {
|
|
84
|
+
const match = group.options.find((option) => option.id === id);
|
|
85
|
+
if (match) return match;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
/** Title rendered for the document segment of the breadcrumb. */
|
|
89
|
+
const documentTitle = computed(() => {
|
|
90
|
+
const item = activeItem.value;
|
|
91
|
+
if (item) return item.title;
|
|
92
|
+
return (__props.app.store.value?.workspace.activeDocument)?.info?.title ?? "";
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Options passed to the combobox. We extend the base option shape with the
|
|
96
|
+
* extra metadata the row template needs:
|
|
97
|
+
* - `isLatest` toggles the "Latest" badge on the most recent version.
|
|
98
|
+
* - `status` drives the row icon (synced / push / pull / conflict).
|
|
99
|
+
*
|
|
100
|
+
* `id` must match `SidebarDocumentVersion.key` so emitted updates can
|
|
101
|
+
* resolve back to the underlying version.
|
|
102
|
+
*/
|
|
103
|
+
const versionOptions = computed(() => versions.value.map((v) => ({
|
|
104
|
+
id: v.key,
|
|
105
|
+
label: v.version,
|
|
106
|
+
isLatest: v.isLatest,
|
|
107
|
+
status: v.status
|
|
108
|
+
})));
|
|
109
|
+
/**
|
|
110
|
+
* The combobox compares `modelValue` to options by reference, so we must
|
|
111
|
+
* return the exact option object from `versionOptions` rather than a freshly
|
|
112
|
+
* constructed one — otherwise the active row would never render as selected.
|
|
113
|
+
*/
|
|
114
|
+
const selectedOption = computed(() => {
|
|
115
|
+
const active = activeVersion.value;
|
|
116
|
+
if (!active) return;
|
|
117
|
+
return versionOptions.value.find((option) => option.id === active.key);
|
|
118
|
+
});
|
|
119
|
+
/**
|
|
120
|
+
* True when the current route actually resolves to a document. The workspace
|
|
121
|
+
* store's `activeDocument` getter falls back to the first document in the
|
|
122
|
+
* workspace even while the user is on a settings or environment page, so we
|
|
123
|
+
* key off `activeEntities.documentSlug` instead — it is only populated when
|
|
124
|
+
* the route actually carries a `:documentSlug` segment.
|
|
125
|
+
*/
|
|
126
|
+
const hasActiveDocument = computed(() => Boolean(__props.app.activeEntities.documentSlug.value));
|
|
127
|
+
/**
|
|
128
|
+
* True only for registry-backed documents, which are the only ones that can
|
|
129
|
+
* advertise multiple versions and therefore the only ones that get a picker.
|
|
130
|
+
*/
|
|
131
|
+
const hasVersionPicker = computed(() => Boolean(activeItem.value && activeRegistryMeta.value));
|
|
132
|
+
/** Hide the entire breadcrumb when there is nothing meaningful to show. */
|
|
133
|
+
const isVisible = computed(() => Boolean(workspaceTitle.value || hasActiveDocument.value));
|
|
134
|
+
/** Guards against double-firing the loader when the user clicks repeatedly. */
|
|
135
|
+
const isLoading = ref(false);
|
|
136
|
+
useVersionConflictCheck({
|
|
137
|
+
store: () => __props.app.store.value,
|
|
138
|
+
fetcher: () => __props.fetchRegistryDocument,
|
|
139
|
+
registry: () => activeItem.value?.registry,
|
|
140
|
+
versions
|
|
141
|
+
});
|
|
142
|
+
const navigateToDocument = (documentSlug) => {
|
|
143
|
+
__props.app.eventBus.emit("ui:navigate", {
|
|
144
|
+
page: "document",
|
|
145
|
+
path: "overview",
|
|
146
|
+
documentSlug
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Routes to the get-started page for a workspace, identified by the
|
|
151
|
+
* combobox option id. We resolve the underlying `WorkspaceOption` from
|
|
152
|
+
* `workspaceList` so the team-slug and slug pair is sourced from the same
|
|
153
|
+
* place the picker built its options - that sidesteps any ambiguity if a
|
|
154
|
+
* teamSlug or slug were ever to contain a slash.
|
|
155
|
+
*
|
|
156
|
+
* When team workspaces are enabled and the active team has no real
|
|
157
|
+
* workspace yet, the picker may surface a synthetic placeholder option
|
|
158
|
+
* (id: `getWorkspaceId(teamSlug, DEFAULT_TEAM_WORKSPACE_SLUG)`). We route
|
|
159
|
+
* that through the normal navigation flow so the route handler can create
|
|
160
|
+
* the workspace on demand.
|
|
161
|
+
*/
|
|
162
|
+
const navigateToWorkspaceGetStarted = (workspaceId) => {
|
|
163
|
+
const emitNavigation = (teamSlug, slug) => {
|
|
164
|
+
__props.app.eventBus.emit("ui:navigate", {
|
|
165
|
+
page: "workspace",
|
|
166
|
+
path: "get-started",
|
|
167
|
+
teamSlug,
|
|
168
|
+
workspaceSlug: slug
|
|
169
|
+
});
|
|
170
|
+
};
|
|
171
|
+
const workspace = __props.app.workspace.workspaceList.value?.find((w) => w.id === workspaceId);
|
|
172
|
+
if (workspace) {
|
|
173
|
+
emitNavigation(workspace.teamSlug, workspace.slug);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Click handler for the workspace label when it renders as a plain link
|
|
179
|
+
* (i.e. the user is NOT yet on the get-started page). Navigating there
|
|
180
|
+
* is what flips the same segment into a dropdown on the next render.
|
|
181
|
+
*/
|
|
182
|
+
const handleWorkspaceLinkClick = () => {
|
|
183
|
+
const id = activeWorkspaceId.value;
|
|
184
|
+
if (!id) return;
|
|
185
|
+
navigateToWorkspaceGetStarted(id);
|
|
186
|
+
};
|
|
187
|
+
/**
|
|
188
|
+
* Click handler for the document title segment. Routes back to the
|
|
189
|
+
* document overview - useful when the user is deep inside an operation
|
|
190
|
+
* page and wants to jump back to the top-level doc view without losing
|
|
191
|
+
* the workspace / document context the breadcrumb already encodes.
|
|
192
|
+
*/
|
|
193
|
+
const handleDocumentTitleClick = () => {
|
|
194
|
+
const documentSlug = __props.app.activeEntities.documentSlug.value;
|
|
195
|
+
if (!documentSlug) return;
|
|
196
|
+
navigateToDocument(documentSlug);
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Selecting a workspace from the combobox routes to that workspace's
|
|
200
|
+
* get-started page. We deliberately go to get-started rather than the
|
|
201
|
+
* environment overview because the workspace switch is the natural moment
|
|
202
|
+
* to surface onboarding guidance - the user has effectively arrived at a
|
|
203
|
+
* fresh workspace and may not yet have any documents loaded.
|
|
204
|
+
*/
|
|
205
|
+
const handleWorkspaceSelect = (option) => {
|
|
206
|
+
if (!option) return;
|
|
207
|
+
if (option.id === activeWorkspaceId.value) return;
|
|
208
|
+
navigateToWorkspaceGetStarted(option.id);
|
|
209
|
+
};
|
|
210
|
+
const handleVersionSelect = async (option) => {
|
|
211
|
+
if (!option) return;
|
|
212
|
+
const version = versions.value.find((v) => v.key === option.id);
|
|
213
|
+
if (!version || version.key === activeVersion.value?.key) return;
|
|
214
|
+
if (version.documentName) {
|
|
215
|
+
navigateToDocument(version.documentName);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
const registry = activeItem.value?.registry;
|
|
219
|
+
const workspaceStore = __props.app.store.value;
|
|
220
|
+
if (!registry || !__props.fetchRegistryDocument || !workspaceStore) {
|
|
221
|
+
toast("Cannot load this version without a registry fetcher.", "error");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (isLoading.value) return;
|
|
225
|
+
isLoading.value = true;
|
|
226
|
+
const outcome = await safeRun(() => loadRegistryDocument({
|
|
227
|
+
fetcher: __props.fetchRegistryDocument,
|
|
228
|
+
workspaceStore,
|
|
229
|
+
namespace: registry.namespace,
|
|
230
|
+
slug: registry.slug,
|
|
231
|
+
version: version.version,
|
|
232
|
+
commitHash: version.registryCommitHash
|
|
233
|
+
}));
|
|
234
|
+
isLoading.value = false;
|
|
235
|
+
if (!outcome.ok) {
|
|
236
|
+
toast(outcome.error, "error");
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const result = outcome.data;
|
|
240
|
+
if (!result.ok) {
|
|
241
|
+
toast(result.error, "error");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
navigateToDocument(result.documentName);
|
|
245
|
+
};
|
|
246
|
+
/** Modal lifecycle for the create-new-version flow. */
|
|
247
|
+
const createVersionModal = useModal();
|
|
248
|
+
/**
|
|
249
|
+
* Versions already loaded into the workspace store for the active group.
|
|
250
|
+
* Used to keep the modal from accepting duplicates that would silently
|
|
251
|
+
* collide with an existing local document. Versions advertised only by the
|
|
252
|
+
* registry are intentionally NOT included - submitting one of those is the
|
|
253
|
+
* conflict-resolution path the create-draft flow opts into.
|
|
254
|
+
*/
|
|
255
|
+
const loadedVersionStrings = computed(() => versions.value.filter((v) => Boolean(v.documentName)).map((v) => v.version));
|
|
256
|
+
const handleCreateVersion = async (version) => {
|
|
257
|
+
const registry = activeItem.value?.registry;
|
|
258
|
+
const seedDocumentName = __props.app.activeEntities.documentSlug.value;
|
|
259
|
+
const store = __props.app.store.value;
|
|
260
|
+
if (!registry || !seedDocumentName || !store) {
|
|
261
|
+
toast("Cannot create a new version without an active registry document.", "error");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (isLoading.value) return;
|
|
265
|
+
isLoading.value = true;
|
|
266
|
+
const result = await createDraftRegistryDocument({
|
|
267
|
+
workspaceStore: store,
|
|
268
|
+
namespace: registry.namespace,
|
|
269
|
+
slug: registry.slug,
|
|
270
|
+
version,
|
|
271
|
+
seedDocumentName
|
|
272
|
+
});
|
|
273
|
+
isLoading.value = false;
|
|
274
|
+
if (!result.ok) {
|
|
275
|
+
toast(result.error, "error");
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
navigateToDocument(result.documentName);
|
|
279
|
+
};
|
|
280
|
+
return (_ctx, _cache) => {
|
|
281
|
+
return isVisible.value ? (openBlock(), createElementBlock("nav", _hoisted_1, [
|
|
282
|
+
_cache[6] || (_cache[6] = createElementVNode("span", {
|
|
283
|
+
"aria-hidden": "true",
|
|
284
|
+
class: "bg-border h-4 w-px shrink-0"
|
|
285
|
+
}, null, -1)),
|
|
286
|
+
workspaceTitle.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [!hasActiveDocument.value ? (openBlock(), createBlock(unref(ScalarCombobox), {
|
|
287
|
+
key: 0,
|
|
288
|
+
class: "workspace-picker w-72",
|
|
289
|
+
modelValue: selectedWorkspaceOption.value,
|
|
290
|
+
options: workspaceGroups.value,
|
|
291
|
+
placeholder: "Search workspaces",
|
|
292
|
+
onAdd: _cache[0] || (_cache[0] = ($event) => emit("createWorkspace")),
|
|
293
|
+
"onUpdate:modelValue": handleWorkspaceSelect
|
|
294
|
+
}, {
|
|
295
|
+
add: withCtx(() => [..._cache[2] || (_cache[2] = [createElementVNode("span", { class: "text-c-1" }, "Create local workspace", -1)])]),
|
|
296
|
+
default: withCtx(() => [createElementVNode("button", _hoisted_2, [createElementVNode("span", _hoisted_3, toDisplayString(workspaceTitle.value), 1), createVNode(unref(ScalarIconCaretDown), {
|
|
297
|
+
class: "text-c-3 size-3 shrink-0",
|
|
298
|
+
weight: "bold"
|
|
299
|
+
})])]),
|
|
300
|
+
_: 1
|
|
301
|
+
}, 8, ["modelValue", "options"])) : (openBlock(), createElementBlock("button", {
|
|
302
|
+
key: 1,
|
|
303
|
+
"aria-label": "Open workspace home",
|
|
304
|
+
class: "hover:bg-b-2 truncate rounded px-1.5 py-0.5",
|
|
305
|
+
type: "button",
|
|
306
|
+
onClick: handleWorkspaceLinkClick
|
|
307
|
+
}, toDisplayString(workspaceTitle.value), 1))], 64)) : createCommentVNode("", true),
|
|
308
|
+
hasActiveDocument.value ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
|
|
309
|
+
_cache[5] || (_cache[5] = createElementVNode("span", {
|
|
310
|
+
"aria-hidden": "true",
|
|
311
|
+
class: "text-c-3"
|
|
312
|
+
}, " / ", -1)),
|
|
313
|
+
createElementVNode("button", {
|
|
314
|
+
class: "hover:bg-b-2 truncate rounded px-1.5 py-0.5",
|
|
315
|
+
type: "button",
|
|
316
|
+
onClick: handleDocumentTitleClick
|
|
317
|
+
}, toDisplayString(documentTitle.value), 1),
|
|
318
|
+
hasVersionPicker.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
|
|
319
|
+
_cache[4] || (_cache[4] = createElementVNode("span", {
|
|
320
|
+
"aria-hidden": "true",
|
|
321
|
+
class: "text-c-3"
|
|
322
|
+
}, " / ", -1)),
|
|
323
|
+
createVNode(unref(ScalarCombobox), {
|
|
324
|
+
class: "version-picker w-64",
|
|
325
|
+
modelValue: selectedOption.value,
|
|
326
|
+
options: versionOptions.value,
|
|
327
|
+
placeholder: "Search versions",
|
|
328
|
+
onAdd: _cache[1] || (_cache[1] = ($event) => unref(createVersionModal).show()),
|
|
329
|
+
"onUpdate:modelValue": handleVersionSelect
|
|
330
|
+
}, {
|
|
331
|
+
option: withCtx(({ option, selected }) => [
|
|
332
|
+
(openBlock(), createBlock(resolveDynamicComponent(unref(VERSION_STATUS_PRESENTATION)[option.status].icon), {
|
|
333
|
+
"aria-label": unref(VERSION_STATUS_PRESENTATION)[option.status].label,
|
|
334
|
+
class: normalizeClass(["size-4 shrink-0", unref(VERSION_STATUS_PRESENTATION)[option.status].class]),
|
|
335
|
+
title: unref(VERSION_STATUS_PRESENTATION)[option.status].label
|
|
336
|
+
}, null, 8, [
|
|
337
|
+
"aria-label",
|
|
338
|
+
"class",
|
|
339
|
+
"title"
|
|
340
|
+
])),
|
|
341
|
+
createElementVNode("span", { class: normalizeClass(["text-c-1 min-w-0 flex-1 truncate", { "font-medium": selected }]) }, toDisplayString(option.label), 3),
|
|
342
|
+
option.isLatest ? (openBlock(), createElementBlock("span", _hoisted_5, " Latest ")) : createCommentVNode("", true)
|
|
343
|
+
]),
|
|
344
|
+
add: withCtx(() => [..._cache[3] || (_cache[3] = [createElementVNode("span", { class: "text-c-1 font-medium" }, "New Version", -1)])]),
|
|
345
|
+
default: withCtx(() => [createElementVNode("button", {
|
|
346
|
+
"aria-label": "Document version",
|
|
347
|
+
class: "hover:bg-b-2 flex items-center gap-1 rounded px-1.5 py-0.5 disabled:opacity-50",
|
|
348
|
+
disabled: isLoading.value,
|
|
349
|
+
type: "button"
|
|
350
|
+
}, [createElementVNode("span", null, toDisplayString(unref(activeVersion)?.version ?? ""), 1), createVNode(unref(ScalarIconCaretDown), {
|
|
351
|
+
class: "text-c-3 size-3",
|
|
352
|
+
weight: "bold"
|
|
353
|
+
})], 8, _hoisted_4)]),
|
|
354
|
+
_: 1
|
|
355
|
+
}, 8, ["modelValue", "options"]),
|
|
356
|
+
isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_6, " Loading… ")) : createCommentVNode("", true)
|
|
357
|
+
], 64)) : createCommentVNode("", true)
|
|
358
|
+
], 64)) : createCommentVNode("", true),
|
|
359
|
+
createVNode(CreateVersionModal_default, {
|
|
360
|
+
existingVersions: loadedVersionStrings.value,
|
|
361
|
+
sourceVersion: unref(activeVersion)?.version,
|
|
362
|
+
state: unref(createVersionModal),
|
|
363
|
+
onCreate: handleCreateVersion
|
|
364
|
+
}, null, 8, [
|
|
365
|
+
"existingVersions",
|
|
366
|
+
"sourceVersion",
|
|
367
|
+
"state"
|
|
368
|
+
])
|
|
369
|
+
])) : createCommentVNode("", true);
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
//#endregion
|
|
374
|
+
export { DocumentBreadcrumb_vue_vue_type_script_setup_true_lang_default as default };
|
|
375
|
+
|
|
376
|
+
//# sourceMappingURL=DocumentBreadcrumb.vue.script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentBreadcrumb.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/DocumentBreadcrumb.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarCombobox,\n useModal,\n type ScalarComboboxOption,\n type ScalarComboboxOptionGroup,\n} from '@scalar/components'\nimport { ScalarIconCaretDown } from '@scalar/icons'\nimport { useToasts } from '@scalar/use-toasts'\nimport { getWorkspaceId } from '@scalar/workspace-store/persistence'\nimport { computed, ref } from 'vue'\n\nimport {\n DEFAULT_TEAM_WORKSPACE_SLUG,\n TEAM_WORKSPACES_ENABLED,\n type AppState,\n} from '@/v2/features/app/app-state'\nimport type { VersionStatus } from '@/v2/features/app/helpers/compute-version-status'\nimport { createDraftRegistryDocument } from '@/v2/features/app/helpers/create-draft-registry-document'\nimport { loadRegistryDocument } from '@/v2/features/app/helpers/load-registry-document'\nimport { VERSION_STATUS_PRESENTATION } from '@/v2/features/app/helpers/version-status-presentation'\nimport { useActiveDocumentVersion } from '@/v2/features/app/hooks/use-active-document-version'\nimport type { RegistryDocumentsState } from '@/v2/features/app/hooks/use-sidebar-documents'\nimport { useVersionConflictCheck } from '@/v2/features/app/hooks/use-version-conflict-check'\nimport { safeRun } from '@/v2/helpers/safe-run'\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nimport CreateVersionModal from './CreateVersionModal.vue'\n\nconst {\n app,\n registryDocuments = { status: 'success', documents: [] },\n fetchRegistryDocument,\n} = defineProps<{\n /** The app state used to read the active document and emit navigation events. */\n app: AppState\n /**\n * The list of all available registry documents. Used to merge advertised\n * versions with loaded workspace documents so the picker can offer every\n * known version of the active document.\n */\n registryDocuments?: RegistryDocumentsState\n /**\n * Fetcher used to import a version from the registry when the user picks\n * one that has not been loaded into the local workspace store yet.\n */\n fetchRegistryDocument?: ImportDocumentFromRegistry\n}>()\n\nconst emit = defineEmits<{\n /**\n * Emitted when the user clicks the \"+\" affordance inside the workspace\n * picker dropdown. The parent owns the create-workspace modal so the\n * breadcrumb stays free of workspace lifecycle concerns.\n */\n (event: 'createWorkspace'): void\n}>()\n\nconst { toast } = useToasts()\n\n/**\n * Resolve the active document group, its versions, and the currently\n * selected version once. The right-side sync indicator consumes the same\n * composable so the two surfaces always agree on what \"active\" means.\n */\nconst { activeRegistryMeta, activeItem, versions, activeVersion } =\n useActiveDocumentVersion({\n app,\n registryDocuments: () => registryDocuments,\n })\n\n/** Workspace label rendered as the first segment of the breadcrumb. */\nconst workspaceTitle = computed(\n () => app.workspace.activeWorkspace.value?.label ?? '',\n)\n\n/** Identifier for the active workspace - used as the combobox selection. */\nconst activeWorkspaceId = computed(\n () => app.workspace.activeWorkspace.value?.id,\n)\n\n/**\n * Workspaces grouped by team, mapped into the shape `ScalarCombobox`\n * expects. We always render this as a grouped combobox - even when there\n * is only one group - so the dropdown layout stays consistent and the\n * group label clearly separates `Team Workspaces` from `Local Workspaces`.\n */\nconst workspaceGroups = computed<ScalarComboboxOptionGroup[]>(() =>\n app.workspace.workspaceGroups.value.map((group) => ({\n // `WorkspaceGroup.label` is optional, but the combobox requires a\n // string. Falling back to an empty string lets the option group still\n // render its rows; the `group` slot below renders nothing for empty\n // labels so we do not surface an awkward blank header.\n label: group.label ?? '',\n options: group.options.map((option) => ({\n id: option.id,\n label: option.label,\n })),\n })),\n)\n\n/**\n * Currently selected option object. The combobox compares by reference, so\n * we have to look the option up inside `workspaceGroups` rather than build\n * a fresh `{ id, label }` here.\n */\nconst selectedWorkspaceOption = computed<ScalarComboboxOption | undefined>(\n () => {\n const id = activeWorkspaceId.value\n if (!id) {\n return undefined\n }\n for (const group of workspaceGroups.value) {\n const match = group.options.find((option) => option.id === id)\n if (match) {\n return match\n }\n }\n return undefined\n },\n)\n\n/** Title rendered for the document segment of the breadcrumb. */\nconst documentTitle = computed(() => {\n const item = activeItem.value\n if (item) {\n return item.title\n }\n const doc = app.store.value?.workspace.activeDocument\n return doc?.info?.title ?? ''\n})\n\n/**\n * Options passed to the combobox. We extend the base option shape with the\n * extra metadata the row template needs:\n * - `isLatest` toggles the \"Latest\" badge on the most recent version.\n * - `status` drives the row icon (synced / push / pull / conflict).\n *\n * `id` must match `SidebarDocumentVersion.key` so emitted updates can\n * resolve back to the underlying version.\n */\ntype VersionOption = ScalarComboboxOption & {\n isLatest: boolean\n status: VersionStatus\n}\n\nconst versionOptions = computed<VersionOption[]>(() =>\n versions.value.map((v) => ({\n id: v.key,\n label: v.version,\n // `isLatest` is precomputed by the sidebar layer and tracks the latest\n // *registry-advertised* version, not just the first row in the list —\n // drafts surface ahead of registry rows but never claim the badge.\n isLatest: v.isLatest,\n status: v.status,\n })),\n)\n\n/**\n * The combobox compares `modelValue` to options by reference, so we must\n * return the exact option object from `versionOptions` rather than a freshly\n * constructed one — otherwise the active row would never render as selected.\n */\nconst selectedOption = computed<VersionOption | undefined>(() => {\n const active = activeVersion.value\n if (!active) {\n return undefined\n }\n return versionOptions.value.find((option) => option.id === active.key)\n})\n\n/**\n * True when the current route actually resolves to a document. The workspace\n * store's `activeDocument` getter falls back to the first document in the\n * workspace even while the user is on a settings or environment page, so we\n * key off `activeEntities.documentSlug` instead — it is only populated when\n * the route actually carries a `:documentSlug` segment.\n */\nconst hasActiveDocument = computed(() =>\n Boolean(app.activeEntities.documentSlug.value),\n)\n\n/**\n * True only for registry-backed documents, which are the only ones that can\n * advertise multiple versions and therefore the only ones that get a picker.\n */\nconst hasVersionPicker = computed(() =>\n Boolean(activeItem.value && activeRegistryMeta.value),\n)\n\n/** Hide the entire breadcrumb when there is nothing meaningful to show. */\nconst isVisible = computed(() =>\n Boolean(workspaceTitle.value || hasActiveDocument.value),\n)\n\n/** Guards against double-firing the loader when the user clicks repeatedly. */\nconst isLoading = ref(false)\n\n// Run the three-way conflict check for every loaded version of the active\n// document group. We deliberately do not check versions of *other*\n// documents — the breadcrumb only renders the picker for the active one —\n// but every row inside that picker carries a status icon, so we want a\n// fresh result for each of them up front rather than on demand.\nuseVersionConflictCheck({\n store: () => app.store.value,\n fetcher: () => fetchRegistryDocument,\n registry: () => activeItem.value?.registry,\n versions,\n})\n\nconst navigateToDocument = (documentSlug: string) => {\n app.eventBus.emit('ui:navigate', {\n page: 'document',\n path: 'overview',\n documentSlug,\n })\n}\n\n/**\n * Routes to the get-started page for a workspace, identified by the\n * combobox option id. We resolve the underlying `WorkspaceOption` from\n * `workspaceList` so the team-slug and slug pair is sourced from the same\n * place the picker built its options - that sidesteps any ambiguity if a\n * teamSlug or slug were ever to contain a slash.\n *\n * When team workspaces are enabled and the active team has no real\n * workspace yet, the picker may surface a synthetic placeholder option\n * (id: `getWorkspaceId(teamSlug, DEFAULT_TEAM_WORKSPACE_SLUG)`). We route\n * that through the normal navigation flow so the route handler can create\n * the workspace on demand.\n */\nconst navigateToWorkspaceGetStarted = (workspaceId: string) => {\n const emitNavigation = (teamSlug: string, slug: string) => {\n app.eventBus.emit('ui:navigate', {\n page: 'workspace',\n path: 'get-started',\n teamSlug,\n workspaceSlug: slug,\n })\n }\n\n const workspace = app.workspace.workspaceList.value?.find(\n (w) => w.id === workspaceId,\n )\n if (workspace) {\n emitNavigation(workspace.teamSlug, workspace.slug)\n return\n }\n\n if (!TEAM_WORKSPACES_ENABLED) {\n return\n }\n\n const activeTeamSlug = app.activeEntities.teamSlug?.value\n if (\n activeTeamSlug &&\n activeTeamSlug !== 'local' &&\n workspaceId === getWorkspaceId(activeTeamSlug, DEFAULT_TEAM_WORKSPACE_SLUG)\n ) {\n emitNavigation(activeTeamSlug, DEFAULT_TEAM_WORKSPACE_SLUG)\n }\n}\n\n/**\n * Click handler for the workspace label when it renders as a plain link\n * (i.e. the user is NOT yet on the get-started page). Navigating there\n * is what flips the same segment into a dropdown on the next render.\n */\nconst handleWorkspaceLinkClick = () => {\n const id = activeWorkspaceId.value\n if (!id) {\n return\n }\n navigateToWorkspaceGetStarted(id)\n}\n\n/**\n * Click handler for the document title segment. Routes back to the\n * document overview - useful when the user is deep inside an operation\n * page and wants to jump back to the top-level doc view without losing\n * the workspace / document context the breadcrumb already encodes.\n */\nconst handleDocumentTitleClick = () => {\n const documentSlug = app.activeEntities.documentSlug.value\n if (!documentSlug) {\n return\n }\n navigateToDocument(documentSlug)\n}\n\n/**\n * Selecting a workspace from the combobox routes to that workspace's\n * get-started page. We deliberately go to get-started rather than the\n * environment overview because the workspace switch is the natural moment\n * to surface onboarding guidance - the user has effectively arrived at a\n * fresh workspace and may not yet have any documents loaded.\n */\nconst handleWorkspaceSelect = (option: ScalarComboboxOption | undefined) => {\n if (!option) {\n return\n }\n if (option.id === activeWorkspaceId.value) {\n return\n }\n navigateToWorkspaceGetStarted(option.id)\n}\n\nconst handleVersionSelect = async (option: VersionOption | undefined) => {\n if (!option) {\n return\n }\n const version = versions.value.find((v) => v.key === option.id)\n if (!version || version.key === activeVersion.value?.key) {\n return\n }\n\n // Already imported into the workspace store — just route to it. The sidebar\n // active-version selection follows from `activeDocumentSlug` so switching\n // the route is enough to keep both surfaces in sync.\n if (version.documentName) {\n navigateToDocument(version.documentName)\n return\n }\n\n // Capture `app.store.value` into a local so the closure passed to\n // `safeRun` keeps the non-nullable type without needing an assertion,\n // and so a later store swap cannot redirect the in-flight load.\n const registry = activeItem.value?.registry\n const workspaceStore = app.store.value\n if (!registry || !fetchRegistryDocument || !workspaceStore) {\n toast('Cannot load this version without a registry fetcher.', 'error')\n return\n }\n\n if (isLoading.value) {\n return\n }\n\n isLoading.value = true\n\n // The loader's helpers (fetcher, coercion, slug generation) can throw on\n // network failures or unexpected payloads. `safeRun` swallows the\n // exception and surfaces it as an `{ ok: false, error }` result so a\n // single rejection cannot leave the picker permanently disabled.\n const outcome = await safeRun(() =>\n loadRegistryDocument({\n fetcher: fetchRegistryDocument,\n workspaceStore,\n namespace: registry.namespace,\n slug: registry.slug,\n version: version.version,\n // Forward the registry-advertised hash from the picker row. Storing it\n // on the document lets us later detect when the registry has moved on\n // and surface upstream changes.\n commitHash: version.registryCommitHash,\n }),\n )\n\n isLoading.value = 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 navigateToDocument(result.documentName)\n}\n\n/** Modal lifecycle for the create-new-version flow. */\nconst createVersionModal = useModal()\n\n/**\n * Versions already loaded into the workspace store for the active group.\n * Used to keep the modal from accepting duplicates that would silently\n * collide with an existing local document. Versions advertised only by the\n * registry are intentionally NOT included - submitting one of those is the\n * conflict-resolution path the create-draft flow opts into.\n */\nconst loadedVersionStrings = computed(() =>\n versions.value.filter((v) => Boolean(v.documentName)).map((v) => v.version),\n)\n\nconst handleCreateVersion = async (version: string) => {\n const registry = activeItem.value?.registry\n const seedDocumentName = app.activeEntities.documentSlug.value\n const store = app.store.value\n\n if (!registry || !seedDocumentName || !store) {\n toast(\n 'Cannot create a new version without an active registry document.',\n 'error',\n )\n return\n }\n\n if (isLoading.value) {\n return\n }\n\n isLoading.value = true\n\n const result = await createDraftRegistryDocument({\n workspaceStore: store,\n namespace: registry.namespace,\n slug: registry.slug,\n version,\n seedDocumentName,\n })\n\n isLoading.value = false\n\n if (!result.ok) {\n toast(result.error, 'error')\n return\n }\n\n navigateToDocument(result.documentName)\n}\n</script>\n\n<template>\n <nav\n v-if=\"isVisible\"\n aria-label=\"Document breadcrumb\"\n class=\"flex min-w-0 items-center gap-2 text-sm font-medium\">\n <!--\n The breadcrumb leads with a vertical bar to visually divide it from\n the menu trigger to its left. The menu trigger itself surfaces the\n active scope (\"Team\" / \"Local\") so it doubles as the leading\n breadcrumb segment - the bar separates that scope label from the\n workspace selector.\n -->\n <span\n aria-hidden=\"true\"\n class=\"bg-border h-4 w-px shrink-0\" />\n\n <!--\n Workspace segment. Two-step affordance keyed off whether a document\n is active on the route:\n 1. While viewing a document: a plain link that navigates to the\n workspace's get-started page. A single click cannot accidentally\n switch workspaces while the user is mid-task.\n 2. On any workspace-level page (no active document, e.g. the\n get-started or environment pages): the same label expands into\n a `ScalarCombobox` so switching workspaces is one extra click\n from the workspace home, which is the natural moment for that\n decision.\n -->\n <template v-if=\"workspaceTitle\">\n <ScalarCombobox\n v-if=\"!hasActiveDocument\"\n class=\"workspace-picker w-72\"\n :modelValue=\"selectedWorkspaceOption\"\n :options=\"workspaceGroups\"\n placeholder=\"Search workspaces\"\n @add=\"emit('createWorkspace')\"\n @update:modelValue=\"handleWorkspaceSelect\">\n <button\n aria-label=\"Workspace\"\n class=\"hover:bg-b-2 flex min-w-0 items-center gap-1 rounded px-1.5 py-0.5\"\n type=\"button\">\n <span class=\"truncate\">{{ workspaceTitle }}</span>\n <ScalarIconCaretDown\n class=\"text-c-3 size-3 shrink-0\"\n weight=\"bold\" />\n </button>\n <template #add>\n <span class=\"text-c-1\">Create local workspace</span>\n </template>\n </ScalarCombobox>\n <button\n v-else\n aria-label=\"Open workspace home\"\n class=\"hover:bg-b-2 truncate rounded px-1.5 py-0.5\"\n type=\"button\"\n @click=\"handleWorkspaceLinkClick\">\n {{ workspaceTitle }}\n </button>\n </template>\n\n <template v-if=\"hasActiveDocument\">\n <!--\n Forward slashes separate the inner breadcrumb segments because\n they read as a path-like hierarchy (\"Workspace / Document /\n Version\"), in contrast to the leading bar that separates the\n breadcrumb from the menu trigger.\n -->\n <span\n aria-hidden=\"true\"\n class=\"text-c-3\">\n /\n </span>\n <button\n class=\"hover:bg-b-2 truncate rounded px-1.5 py-0.5\"\n type=\"button\"\n @click=\"handleDocumentTitleClick\">\n {{ documentTitle }}\n </button>\n <template v-if=\"hasVersionPicker\">\n <span\n aria-hidden=\"true\"\n class=\"text-c-3\">\n /\n </span>\n <ScalarCombobox\n class=\"version-picker w-64\"\n :modelValue=\"selectedOption\"\n :options=\"versionOptions\"\n placeholder=\"Search versions\"\n @add=\"createVersionModal.show()\"\n @update:modelValue=\"handleVersionSelect\">\n <button\n aria-label=\"Document version\"\n class=\"hover:bg-b-2 flex items-center gap-1 rounded px-1.5 py-0.5 disabled:opacity-50\"\n :disabled=\"isLoading\"\n type=\"button\">\n <span>{{ activeVersion?.version ?? '' }}</span>\n <ScalarIconCaretDown\n class=\"text-c-3 size-3\"\n weight=\"bold\" />\n </button>\n <template #option=\"{ option, selected }\">\n <component\n :is=\"VERSION_STATUS_PRESENTATION[option.status].icon\"\n :aria-label=\"VERSION_STATUS_PRESENTATION[option.status].label\"\n class=\"size-4 shrink-0\"\n :class=\"VERSION_STATUS_PRESENTATION[option.status].class\"\n :title=\"VERSION_STATUS_PRESENTATION[option.status].label\" />\n <span\n class=\"text-c-1 min-w-0 flex-1 truncate\"\n :class=\"{ 'font-medium': selected }\">\n {{ option.label }}\n </span>\n <span\n v-if=\"option.isLatest\"\n class=\"text-c-3 ml-2 shrink-0 text-xs\">\n Latest\n </span>\n </template>\n <!--\n The combobox's built-in `add` slot renders a `+` icon row below\n the version list. Wiring it up here keeps the create-draft\n affordance discoverable inside the same surface where the user\n picks versions, instead of as a separate button next to it.\n -->\n <template #add>\n <span class=\"text-c-1 font-medium\">New Version</span>\n </template>\n </ScalarCombobox>\n <span\n v-if=\"isLoading\"\n class=\"text-c-3 ml-1 text-xs\">\n Loading…\n </span>\n </template>\n </template>\n <CreateVersionModal\n :existingVersions=\"loadedVersionStrings\"\n :sourceVersion=\"activeVersion?.version\"\n :state=\"createVersionModal\"\n @create=\"handleCreateVersion\" />\n </nav>\n</template>\n\n<style scoped>\n/*\n * The combobox option only marks the *active* (hovered / keyboard-focused)\n * row with a background. We also want a persistent highlight on the\n * currently *selected* version so the user can spot it at a glance, even\n * after moving the cursor or arrowing to another row.\n */\n.version-picker :deep([role='option'][aria-selected='true']) {\n background-color: var(--scalar-background-2);\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AppState } from '../../../../v2/features/app/app-state.js';
|
|
2
|
+
import type { RegistryDocumentsState } from '../../../../v2/features/app/hooks/use-sidebar-documents.js';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
/** App state used to read the active document and registry meta. */
|
|
5
|
+
app: AppState;
|
|
6
|
+
/**
|
|
7
|
+
* Registry documents state. Mirrors the prop accepted by
|
|
8
|
+
* `DocumentBreadcrumb` so both header surfaces resolve the same active
|
|
9
|
+
* document group.
|
|
10
|
+
*/
|
|
11
|
+
registryDocuments?: RegistryDocumentsState;
|
|
12
|
+
};
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
16
|
+
//# sourceMappingURL=DocumentSyncIndicator.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentSyncIndicator.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/DocumentSyncIndicator.vue"],"names":[],"mappings":"AA0DA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAA;AAG3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+CAA+C,CAAA;AAE3F,KAAK,WAAW,GAAG;IACf,oEAAoE;IACpE,GAAG,EAAE,QAAQ,CAAA;IACb;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;CAC3C,CAAC;AAqEJ,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import DocumentSyncIndicator_vue_vue_type_script_setup_true_lang_default from "./DocumentSyncIndicator.vue.script.js";
|
|
2
|
+
//#region src/v2/features/app/components/DocumentSyncIndicator.vue
|
|
3
|
+
var DocumentSyncIndicator_default = DocumentSyncIndicator_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { DocumentSyncIndicator_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=DocumentSyncIndicator.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentSyncIndicator.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/DocumentSyncIndicator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport type { AppState } from '@/v2/features/app/app-state'\nimport { VERSION_STATUS_PRESENTATION } from '@/v2/features/app/helpers/version-status-presentation'\nimport { useActiveDocumentVersion } from '@/v2/features/app/hooks/use-active-document-version'\nimport type { RegistryDocumentsState } from '@/v2/features/app/hooks/use-sidebar-documents'\n\nconst { app, registryDocuments = { status: 'success', documents: [] } } =\n defineProps<{\n /** App state used to read the active document and registry meta. */\n app: AppState\n /**\n * Registry documents state. Mirrors the prop accepted by\n * `DocumentBreadcrumb` so both header surfaces resolve the same active\n * document group.\n */\n registryDocuments?: RegistryDocumentsState\n }>()\n\n/**\n * Resolve the currently active version through the shared composable. The\n * indicator never renders a picker — it only mirrors the status icon — so\n * the composable's `activeVersion` is the only field we need.\n *\n * The composable does not run any network checks; the breadcrumb owns\n * `useVersionConflictCheck` and the indicator simply reads back the cached\n * status that flows through `useSidebarDocuments`.\n */\nconst { activeVersion } = useActiveDocumentVersion({\n app,\n registryDocuments: () => registryDocuments,\n})\n\n/** Presentation block (icon, colour, label) for the active version's status. */\nconst presentation = computed(() => {\n const status = activeVersion.value?.status\n if (!status) {\n return undefined\n }\n return VERSION_STATUS_PRESENTATION[status]\n})\n</script>\n\n<template>\n <component\n :is=\"presentation.icon\"\n v-if=\"presentation\"\n :aria-label=\"presentation.label\"\n class=\"size-4 shrink-0\"\n :class=\"presentation.class\"\n :title=\"presentation.label\" />\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { VERSION_STATUS_PRESENTATION } from "../helpers/version-status-presentation.js";
|
|
2
|
+
import { useActiveDocumentVersion } from "../hooks/use-active-document-version.js";
|
|
3
|
+
import { computed, createBlock, createCommentVNode, defineComponent, normalizeClass, openBlock, resolveDynamicComponent } from "vue";
|
|
4
|
+
//#region src/v2/features/app/components/DocumentSyncIndicator.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
+
var DocumentSyncIndicator_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
6
|
+
__name: "DocumentSyncIndicator",
|
|
7
|
+
props: {
|
|
8
|
+
app: {},
|
|
9
|
+
registryDocuments: { default: () => ({
|
|
10
|
+
status: "success",
|
|
11
|
+
documents: []
|
|
12
|
+
}) }
|
|
13
|
+
},
|
|
14
|
+
setup(__props) {
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the currently active version through the shared composable. The
|
|
17
|
+
* indicator never renders a picker — it only mirrors the status icon — so
|
|
18
|
+
* the composable's `activeVersion` is the only field we need.
|
|
19
|
+
*
|
|
20
|
+
* The composable does not run any network checks; the breadcrumb owns
|
|
21
|
+
* `useVersionConflictCheck` and the indicator simply reads back the cached
|
|
22
|
+
* status that flows through `useSidebarDocuments`.
|
|
23
|
+
*/
|
|
24
|
+
const { activeVersion } = useActiveDocumentVersion({
|
|
25
|
+
app: __props.app,
|
|
26
|
+
registryDocuments: () => __props.registryDocuments
|
|
27
|
+
});
|
|
28
|
+
/** Presentation block (icon, colour, label) for the active version's status. */
|
|
29
|
+
const presentation = computed(() => {
|
|
30
|
+
const status = activeVersion.value?.status;
|
|
31
|
+
if (!status) return;
|
|
32
|
+
return VERSION_STATUS_PRESENTATION[status];
|
|
33
|
+
});
|
|
34
|
+
return (_ctx, _cache) => {
|
|
35
|
+
return presentation.value ? (openBlock(), createBlock(resolveDynamicComponent(presentation.value.icon), {
|
|
36
|
+
key: 0,
|
|
37
|
+
"aria-label": presentation.value.label,
|
|
38
|
+
class: normalizeClass(["size-4 shrink-0", presentation.value.class]),
|
|
39
|
+
title: presentation.value.label
|
|
40
|
+
}, null, 8, [
|
|
41
|
+
"aria-label",
|
|
42
|
+
"class",
|
|
43
|
+
"title"
|
|
44
|
+
])) : createCommentVNode("", true);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
49
|
+
export { DocumentSyncIndicator_vue_vue_type_script_setup_true_lang_default as default };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=DocumentSyncIndicator.vue.script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DocumentSyncIndicator.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/DocumentSyncIndicator.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport type { AppState } from '@/v2/features/app/app-state'\nimport { VERSION_STATUS_PRESENTATION } from '@/v2/features/app/helpers/version-status-presentation'\nimport { useActiveDocumentVersion } from '@/v2/features/app/hooks/use-active-document-version'\nimport type { RegistryDocumentsState } from '@/v2/features/app/hooks/use-sidebar-documents'\n\nconst { app, registryDocuments = { status: 'success', documents: [] } } =\n defineProps<{\n /** App state used to read the active document and registry meta. */\n app: AppState\n /**\n * Registry documents state. Mirrors the prop accepted by\n * `DocumentBreadcrumb` so both header surfaces resolve the same active\n * document group.\n */\n registryDocuments?: RegistryDocumentsState\n }>()\n\n/**\n * Resolve the currently active version through the shared composable. The\n * indicator never renders a picker — it only mirrors the status icon — so\n * the composable's `activeVersion` is the only field we need.\n *\n * The composable does not run any network checks; the breadcrumb owns\n * `useVersionConflictCheck` and the indicator simply reads back the cached\n * status that flows through `useSidebarDocuments`.\n */\nconst { activeVersion } = useActiveDocumentVersion({\n app,\n registryDocuments: () => registryDocuments,\n})\n\n/** Presentation block (icon, colour, label) for the active version's status. */\nconst presentation = computed(() => {\n const status = activeVersion.value?.status\n if (!status) {\n return undefined\n }\n return VERSION_STATUS_PRESENTATION[status]\n})\n</script>\n\n<template>\n <component\n :is=\"presentation.icon\"\n v-if=\"presentation\"\n :aria-label=\"presentation.label\"\n class=\"size-4 shrink-0\"\n :class=\"presentation.class\"\n :title=\"presentation.label\" />\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EA6BA,MAAM,EAAE,kBAAkB,yBAAyB;GACjD,KAAE,QAAA;GACF,yBAAyB,QAAA;GAC1B,CAAA;;EAGD,MAAM,eAAe,eAAe;GAClC,MAAM,SAAS,cAAc,OAAO;AACpC,OAAI,CAAC,OACH;AAEF,UAAO,4BAA4B;IACpC;;UAMS,aAAA,SAAA,WAAA,EAFR,YAMgC,wBALzB,aAAA,MAAa,KAAI,EAAA;;IAErB,cAAY,aAAA,MAAa;IAC1B,OAAK,eAAA,CAAC,mBACE,aAAA,MAAa,MAAK,CAAA;IACzB,OAAO,aAAA,MAAa"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type DraggingItem, type HoveredItem } from '@scalar/sidebar';
|
|
2
|
+
import type { TraversedEntry } from '@scalar/workspace-store/schemas/navigation';
|
|
3
|
+
import type { SidebarDocumentItem } from '../../../../v2/features/app/hooks/use-sidebar-documents.js';
|
|
4
|
+
type __VLS_Props = {
|
|
5
|
+
/** The document row to render. */
|
|
6
|
+
item: SidebarDocumentItem;
|
|
7
|
+
/** Whether the document row is the currently active sidebar entry. */
|
|
8
|
+
active: boolean;
|
|
9
|
+
/** Whether the document is drilled-in (its children are shown). */
|
|
10
|
+
open: boolean;
|
|
11
|
+
/** Whether the document is currently being fetched from the registry. */
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
/** Predicate to check whether a child entry can be dropped on a target. */
|
|
14
|
+
isDroppable: (draggingItem: DraggingItem, hoveredItem: HoveredItem) => boolean;
|
|
15
|
+
/** Predicate to check whether a child entry is currently expanded. */
|
|
16
|
+
isExpanded: (id: string) => boolean;
|
|
17
|
+
/** Predicate to check whether a child entry is currently selected. */
|
|
18
|
+
isSelected: (id: string) => boolean;
|
|
19
|
+
};
|
|
20
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
21
|
+
search: () => any;
|
|
22
|
+
click: (item: SidebarDocumentItem) => any;
|
|
23
|
+
selectItem: (id: string) => any;
|
|
24
|
+
toggleGroup: (id: string) => any;
|
|
25
|
+
back: () => any;
|
|
26
|
+
openSettings: () => any;
|
|
27
|
+
createOperation: (item: SidebarDocumentItem) => any;
|
|
28
|
+
addEmptyFolder: (item: TraversedEntry) => any;
|
|
29
|
+
openMenu: (nativeEvent: MouseEvent | KeyboardEvent, entry: TraversedEntry) => any;
|
|
30
|
+
dragEnd: (draggingItem: DraggingItem, hoveredItem: HoveredItem) => any;
|
|
31
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
32
|
+
onSearch?: (() => any) | undefined;
|
|
33
|
+
onClick?: ((item: SidebarDocumentItem) => any) | undefined;
|
|
34
|
+
onSelectItem?: ((id: string) => any) | undefined;
|
|
35
|
+
onToggleGroup?: ((id: string) => any) | undefined;
|
|
36
|
+
onBack?: (() => any) | undefined;
|
|
37
|
+
onOpenSettings?: (() => any) | undefined;
|
|
38
|
+
onCreateOperation?: ((item: SidebarDocumentItem) => any) | undefined;
|
|
39
|
+
onAddEmptyFolder?: ((item: TraversedEntry) => any) | undefined;
|
|
40
|
+
onOpenMenu?: ((nativeEvent: MouseEvent | KeyboardEvent, entry: TraversedEntry) => any) | undefined;
|
|
41
|
+
onDragEnd?: ((draggingItem: DraggingItem, hoveredItem: HoveredItem) => any) | undefined;
|
|
42
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
43
|
+
declare const _default: typeof __VLS_export;
|
|
44
|
+
export default _default;
|
|
45
|
+
//# sourceMappingURL=SidebarDocument.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SidebarDocument.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/SidebarDocument.vue"],"names":[],"mappings":"AA+MA,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAA;AAEhF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAA;AAExF,KAAK,WAAW,GAAG;IACjB,kCAAkC;IAClC,IAAI,EAAE,mBAAmB,CAAA;IACzB,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAA;IACf,mEAAmE;IACnE,IAAI,EAAE,OAAO,CAAA;IACb,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,2EAA2E;IAC3E,WAAW,EAAE,CAAC,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,KAAK,OAAO,CAAA;IAC9E,sEAAsE;IACtE,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAA;IACnC,sEAAsE;IACtE,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAA;CACpC,CAAC;AAkcF,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import SidebarDocument_vue_vue_type_script_setup_true_lang_default from "./SidebarDocument.vue.script.js";
|
|
2
|
+
//#region src/v2/features/app/components/SidebarDocument.vue
|
|
3
|
+
var SidebarDocument_default = SidebarDocument_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { SidebarDocument_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=SidebarDocument.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SidebarDocument.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/SidebarDocument.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {\n ScalarIconButton,\n ScalarSidebarButton,\n ScalarSidebarItem,\n ScalarSidebarNestedItems,\n} from '@scalar/components'\nimport {\n ScalarIconCaretLeft,\n ScalarIconDotsThree,\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 type { TraversedEntry } from '@scalar/workspace-store/schemas/navigation'\n\nimport type { SidebarDocumentItem } from '@/v2/features/app/hooks/use-sidebar-documents'\n\nconst { item, active, open, loading } = defineProps<{\n /** The document row to render. */\n item: SidebarDocumentItem\n /** Whether the document row is the currently active sidebar entry. */\n active: boolean\n /** Whether the document is drilled-in (its children are shown). */\n open: boolean\n /** Whether the document is currently being fetched from the registry. */\n loading?: boolean\n /** Predicate to check whether a child entry can be dropped on a target. */\n isDroppable: (draggingItem: DraggingItem, hoveredItem: HoveredItem) => boolean\n /** Predicate to check whether a child entry is currently expanded. */\n isExpanded: (id: string) => boolean\n /** Predicate to check whether a child entry is currently selected. */\n isSelected: (id: string) => boolean\n}>()\n\nconst emit = defineEmits<{\n /** Navigate back from the drilled-in document view. */\n (event: 'back'): void\n /** The document row was clicked. */\n (event: 'click', item: SidebarDocumentItem): void\n /** Open the document (collection) settings. */\n (event: 'openSettings'): void\n /** Open the per-document search modal. */\n (event: 'search'): void\n /** Create a new operation in this document. */\n (event: 'createOperation', item: SidebarDocumentItem): void\n /** Add an operation inside an empty tag/folder. */\n (event: 'addEmptyFolder', item: TraversedEntry): void\n /** Open the contextual \"more\" menu for a child entry. */\n (\n event: 'openMenu',\n nativeEvent: MouseEvent | KeyboardEvent,\n entry: TraversedEntry,\n ): void\n /** A child entry was selected. */\n (event: 'selectItem', id: string): void\n /** A child group was toggled. */\n (event: 'toggleGroup', id: string): void\n /** A drag-and-drop operation has completed. */\n (\n event: 'dragEnd',\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n ): boolean\n}>()\n\nconst handleDragEnd = (\n draggingItem: DraggingItem,\n hoveredItem: HoveredItem,\n): boolean => emit('dragEnd', draggingItem, hoveredItem)\n</script>\n\n<template>\n <ScalarSidebarNestedItems\n :active=\"active\"\n controlled\n :open=\"open\"\n @back=\"emit('back')\"\n @click=\"emit('click', item)\">\n <span>{{ item.title }}</span>\n <template\n v-if=\"loading\"\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=\"emit('back')\">\n <template #icon>\n <ScalarIconCaretLeft 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=\"emit('openSettings')\" />\n <ScalarIconButton\n :icon=\"ScalarIconMagnifyingGlass\"\n label=\"Search collection\"\n size=\"sm\"\n @click=\"emit('search')\" />\n <ScalarIconButton\n class=\"rounded-full border\"\n :icon=\"ScalarIconPlus\"\n label=\"Add operation\"\n size=\"sm\"\n @click=\"emit('createOperation', 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('client', item.navigation.children)\"\n :key=\"child.id\"\n :isDroppable=\"isDroppable\"\n :isExpanded=\"isExpanded\"\n :isSelected=\"isSelected\"\n :item=\"child\"\n layout=\"client\"\n @onDragEnd=\"handleDragEnd\"\n @selectItem=\"(id: string) => emit('selectItem', id)\"\n @toggleGroup=\"(id: string) => emit('toggleGroup', id)\">\n <!--\n Per-item \"more\" dropdown for tags, operations and examples\n (add / edit / delete…). The dropdown is rendered once at the\n sidebar root and anchors itself to whichever icon button opened\n it. Operation settings live on the operation header (next to the\n 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=\"(e: MouseEvent) => emit('openMenu', e, entry)\"\n @keydown.down.stop=\"\n (e: KeyboardEvent) => emit('openMenu', e, entry)\n \"\n @keydown.enter.stop=\"\n (e: KeyboardEvent) => emit('openMenu', e, entry)\n \"\n @keydown.space.stop=\"\n (e: KeyboardEvent) => emit('openMenu', e, entry)\n \"\n @keydown.up.stop=\"\n (e: KeyboardEvent) => emit('openMenu', e, entry)\n \" />\n </template>\n <!--\n Empty tag / folder slot. Matches the \"Add operation\" affordance\n from the old sidebar so users can create an operation directly\n inside the hovered tag.\n -->\n <template #empty=\"{ item: emptyItem }\">\n <ScalarSidebarItem\n is=\"button\"\n @click=\"emit('addEmptyFolder', 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"],"mappings":""}
|