@scalar/api-client 3.5.1 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +0 -1
  5. package/dist/style.css +3933 -4768
  6. package/dist/styles/tailwind.config.css +20 -0
  7. package/dist/styles/utilities.css +45 -0
  8. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.d.ts.map +1 -1
  9. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js +1 -1
  10. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js.map +1 -1
  11. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js +1 -1
  12. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js.map +1 -1
  13. package/dist/v2/blocks/request-block/RequestBlock.vue.script.js.map +1 -1
  14. package/dist/v2/blocks/scalar-auth-selector-block/components/AuthSelector.vue.script.js.map +1 -1
  15. package/dist/v2/components/data-table/DataTableInput.vue.d.ts +1 -1
  16. package/dist/v2/components/data-table/DataTableInput.vue.d.ts.map +1 -1
  17. package/dist/v2/constants.js +1 -1
  18. package/dist/v2/features/app/App.vue.d.ts +15 -31
  19. package/dist/v2/features/app/App.vue.d.ts.map +1 -1
  20. package/dist/v2/features/app/App.vue.js.map +1 -1
  21. package/dist/v2/features/app/App.vue.script.js +131 -50
  22. package/dist/v2/features/app/App.vue.script.js.map +1 -1
  23. package/dist/v2/features/app/app-state.d.ts +10 -14
  24. package/dist/v2/features/app/app-state.d.ts.map +1 -1
  25. package/dist/v2/features/app/app-state.js +53 -21
  26. package/dist/v2/features/app/app-state.js.map +1 -1
  27. package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
  28. package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
  29. package/dist/v2/features/app/components/AppHeader.vue.script.js +4 -3
  30. package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
  31. package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts +32 -0
  32. package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts.map +1 -0
  33. package/dist/v2/features/app/components/AppHeaderActions.vue.js +7 -0
  34. package/dist/v2/features/app/components/AppHeaderActions.vue.js.map +1 -0
  35. package/dist/v2/features/app/components/AppHeaderActions.vue.script.js +172 -0
  36. package/dist/v2/features/app/components/AppHeaderActions.vue.script.js.map +1 -0
  37. package/dist/v2/features/app/components/AppSidebar.vue.d.ts +2 -5
  38. package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
  39. package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
  40. package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
  41. package/dist/v2/features/app/components/AppSidebar.vue.script.js +4 -9
  42. package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
  43. package/dist/v2/features/app/components/DesktopTabs.vue.d.ts.map +1 -1
  44. package/dist/v2/features/app/components/DesktopTabs.vue.js.map +1 -1
  45. package/dist/v2/features/app/components/DesktopTabs.vue.script.js +2 -2
  46. package/dist/v2/features/app/components/DesktopTabs.vue.script.js.map +1 -1
  47. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts +1 -2
  48. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts.map +1 -1
  49. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js +1 -1
  50. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js.map +1 -1
  51. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js +4 -35
  52. package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js.map +1 -1
  53. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts +1 -1
  54. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts.map +1 -1
  55. package/dist/v2/features/app/components/PublishDocumentModal.vue.d.ts +77 -0
  56. package/dist/v2/features/app/components/PublishDocumentModal.vue.d.ts.map +1 -0
  57. package/dist/v2/features/app/components/PublishDocumentModal.vue.js +7 -0
  58. package/dist/v2/features/app/components/PublishDocumentModal.vue.js.map +1 -0
  59. package/dist/v2/features/app/components/PublishDocumentModal.vue.script.js +209 -0
  60. package/dist/v2/features/app/components/PublishDocumentModal.vue.script.js.map +1 -0
  61. package/dist/v2/features/app/components/SyncConflictResolutionEditor.vue.d.ts.map +1 -0
  62. package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.js +2 -2
  63. package/dist/v2/features/app/components/SyncConflictResolutionEditor.vue.js.map +1 -0
  64. package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.script.js +1 -1
  65. package/dist/v2/features/{collection/components/SyncConflictResolutionEditor.vue.js.map → app/components/SyncConflictResolutionEditor.vue.script.js.map} +1 -1
  66. package/dist/v2/features/app/helpers/check-version-conflict.d.ts +8 -5
  67. package/dist/v2/features/app/helpers/check-version-conflict.d.ts.map +1 -1
  68. package/dist/v2/features/app/helpers/check-version-conflict.js +10 -7
  69. package/dist/v2/features/app/helpers/check-version-conflict.js.map +1 -1
  70. package/dist/v2/features/app/helpers/create-api-client-app.d.ts +8 -5
  71. package/dist/v2/features/app/helpers/create-api-client-app.d.ts.map +1 -1
  72. package/dist/v2/features/app/helpers/create-api-client-app.js +2 -2
  73. package/dist/v2/features/app/helpers/create-api-client-app.js.map +1 -1
  74. package/dist/v2/features/app/helpers/load-registry-document.d.ts +1 -10
  75. package/dist/v2/features/app/helpers/load-registry-document.d.ts.map +1 -1
  76. package/dist/v2/features/app/helpers/load-registry-document.js +6 -5
  77. package/dist/v2/features/app/helpers/load-registry-document.js.map +1 -1
  78. package/dist/v2/features/app/helpers/registry-error-messages.d.ts +23 -0
  79. package/dist/v2/features/app/helpers/registry-error-messages.d.ts.map +1 -0
  80. package/dist/v2/features/app/helpers/registry-error-messages.js +63 -0
  81. package/dist/v2/features/app/helpers/registry-error-messages.js.map +1 -0
  82. package/dist/v2/features/app/hooks/use-active-document-version.d.ts +2 -1
  83. package/dist/v2/features/app/hooks/use-active-document-version.d.ts.map +1 -1
  84. package/dist/v2/features/app/hooks/use-active-document-version.js.map +1 -1
  85. package/dist/v2/features/app/hooks/use-document-sync.d.ts +126 -0
  86. package/dist/v2/features/app/hooks/use-document-sync.d.ts.map +1 -0
  87. package/dist/v2/features/app/hooks/use-document-sync.js +448 -0
  88. package/dist/v2/features/app/hooks/use-document-sync.js.map +1 -0
  89. package/dist/v2/features/app/hooks/use-network-status.d.ts +29 -0
  90. package/dist/v2/features/app/hooks/use-network-status.d.ts.map +1 -0
  91. package/dist/v2/features/app/hooks/use-network-status.js +58 -0
  92. package/dist/v2/features/app/hooks/use-network-status.js.map +1 -0
  93. package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts +1 -25
  94. package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts.map +1 -1
  95. package/dist/v2/features/app/hooks/use-sidebar-documents.js.map +1 -1
  96. package/dist/v2/features/app/index.d.ts +1 -1
  97. package/dist/v2/features/app/index.d.ts.map +1 -1
  98. package/dist/v2/features/collection/DocumentCollection.vue.d.ts.map +1 -1
  99. package/dist/v2/features/collection/DocumentCollection.vue.js.map +1 -1
  100. package/dist/v2/features/collection/DocumentCollection.vue.script.js +43 -277
  101. package/dist/v2/features/collection/DocumentCollection.vue.script.js.map +1 -1
  102. package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.d.ts.map +1 -1
  103. package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.js.map +1 -1
  104. package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.script.js +35 -17
  105. package/dist/v2/features/command-palette/components/CommandPaletteExample.vue.script.js.map +1 -1
  106. package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.d.ts.map +1 -1
  107. package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.js.map +1 -1
  108. package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.script.js +15 -13
  109. package/dist/v2/features/command-palette/components/CommandPaletteImport.vue.script.js.map +1 -1
  110. package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.d.ts.map +1 -1
  111. package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.js +1 -1
  112. package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.js.map +1 -1
  113. package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.script.js +51 -39
  114. package/dist/v2/features/command-palette/components/CommandPaletteImportCurl.vue.script.js.map +1 -1
  115. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.d.ts.map +1 -1
  116. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.js.map +1 -1
  117. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js +30 -14
  118. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js.map +1 -1
  119. package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.d.ts.map +1 -1
  120. package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.js.map +1 -1
  121. package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.script.js +44 -42
  122. package/dist/v2/features/command-palette/components/CommandPaletteRequest.vue.script.js.map +1 -1
  123. package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.d.ts.map +1 -1
  124. package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.js.map +1 -1
  125. package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.script.js +33 -17
  126. package/dist/v2/features/command-palette/components/CommandPaletteTag.vue.script.js.map +1 -1
  127. package/dist/v2/features/command-palette/helpers/load-document-from-source.d.ts.map +1 -1
  128. package/dist/v2/features/command-palette/helpers/load-document-from-source.js +3 -2
  129. package/dist/v2/features/command-palette/helpers/load-document-from-source.js.map +1 -1
  130. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.d.ts.map +1 -1
  131. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js +5 -5
  132. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js.map +1 -1
  133. package/dist/v2/helpers/is-url.d.ts.map +1 -1
  134. package/dist/v2/helpers/is-url.js +2 -1
  135. package/dist/v2/helpers/is-url.js.map +1 -1
  136. package/dist/v2/types/configuration.d.ts +273 -7
  137. package/dist/v2/types/configuration.d.ts.map +1 -1
  138. package/dist/vue-styles.css +1389 -0
  139. package/package.json +20 -14
  140. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js +0 -7
  141. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js.map +0 -1
  142. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js +0 -51
  143. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js.map +0 -1
  144. package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.d.ts.map +0 -1
  145. package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.script.js.map +0 -1
  146. /package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.d.ts +0 -0
@@ -0,0 +1,172 @@
1
+ import { Fragment, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, openBlock, unref, withCtx } from "vue";
2
+ import { ScalarButton } from "@scalar/components";
3
+ import { ScalarIconArrowCounterClockwise, ScalarIconCloudArrowDown, ScalarIconCloudArrowUp, ScalarIconCloudSlash, ScalarIconFloppyDisk } from "@scalar/icons";
4
+ //#region src/v2/features/app/components/AppHeaderActions.vue?vue&type=script&setup=true&lang.ts
5
+ var AppHeaderActions_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
6
+ __name: "AppHeaderActions",
7
+ props: {
8
+ showLocalSaveActions: { type: Boolean },
9
+ showTeamSyncActions: { type: Boolean },
10
+ showTeamPublishAction: { type: Boolean },
11
+ isActiveDocumentDirty: { type: Boolean },
12
+ isOffline: { type: Boolean },
13
+ canPullActiveDocument: { type: Boolean },
14
+ canPushActiveDocument: { type: Boolean }
15
+ },
16
+ emits: [
17
+ "save",
18
+ "revert",
19
+ "pull",
20
+ "push",
21
+ "publish"
22
+ ],
23
+ setup(__props, { emit: __emit }) {
24
+ const emit = __emit;
25
+ return (_ctx, _cache) => {
26
+ return openBlock(), createElementBlock(Fragment, null, [
27
+ __props.showLocalSaveActions ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [__props.isActiveDocumentDirty ? (openBlock(), createBlock(unref(ScalarButton), {
28
+ key: 0,
29
+ "aria-label": "Revert changes",
30
+ class: "text-c-2 hover:text-c-1 size-6 shrink-0 p-0",
31
+ "data-testid": "app-header-revert-button",
32
+ size: "xs",
33
+ type: "button",
34
+ variant: "ghost",
35
+ onClick: _cache[0] || (_cache[0] = ($event) => emit("revert"))
36
+ }, {
37
+ default: withCtx(() => [createVNode(unref(ScalarIconArrowCounterClockwise), {
38
+ class: "size-3.5",
39
+ size: "sm",
40
+ thickness: "1.5"
41
+ })]),
42
+ _: 1
43
+ })) : createCommentVNode("", true), createVNode(unref(ScalarButton), {
44
+ "aria-label": "Save",
45
+ class: "shrink-0 gap-1.5",
46
+ "data-testid": "app-header-save-button",
47
+ disabled: !__props.isActiveDocumentDirty,
48
+ size: "xs",
49
+ title: "Save",
50
+ type: "button",
51
+ variant: "solid",
52
+ onClick: _cache[1] || (_cache[1] = ($event) => emit("save"))
53
+ }, {
54
+ default: withCtx(() => [createVNode(unref(ScalarIconFloppyDisk), {
55
+ class: "size-3.5",
56
+ size: "sm",
57
+ thickness: "1.5"
58
+ }), _cache[6] || (_cache[6] = createElementVNode("span", { class: "max-md:hidden" }, "Save", -1))]),
59
+ _: 1
60
+ }, 8, ["disabled"])], 64)) : createCommentVNode("", true),
61
+ __props.showTeamSyncActions ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [
62
+ __props.isActiveDocumentDirty ? (openBlock(), createBlock(unref(ScalarButton), {
63
+ key: 0,
64
+ "aria-label": "Revert changes",
65
+ class: "text-c-2 hover:text-c-1 size-6 shrink-0 p-0",
66
+ "data-testid": "app-header-revert-button",
67
+ size: "xs",
68
+ type: "button",
69
+ variant: "ghost",
70
+ onClick: _cache[2] || (_cache[2] = ($event) => emit("revert"))
71
+ }, {
72
+ default: withCtx(() => [createVNode(unref(ScalarIconArrowCounterClockwise), {
73
+ class: "size-3.5",
74
+ size: "sm",
75
+ thickness: "1.5"
76
+ })]),
77
+ _: 1
78
+ })) : createCommentVNode("", true),
79
+ createVNode(unref(ScalarButton), {
80
+ "aria-label": __props.isOffline ? "Pull (offline)" : "Pull",
81
+ class: "shrink-0 gap-1.5",
82
+ "data-testid": "app-header-pull-button",
83
+ disabled: !__props.canPullActiveDocument,
84
+ size: "xs",
85
+ title: __props.isOffline ? "You are offline." : "Pull",
86
+ type: "button",
87
+ variant: "solid",
88
+ onClick: _cache[3] || (_cache[3] = ($event) => emit("pull"))
89
+ }, {
90
+ default: withCtx(() => [__props.isOffline ? (openBlock(), createBlock(unref(ScalarIconCloudSlash), {
91
+ key: 0,
92
+ class: "size-3.5",
93
+ size: "sm",
94
+ thickness: "1.5"
95
+ })) : (openBlock(), createBlock(unref(ScalarIconCloudArrowDown), {
96
+ key: 1,
97
+ class: "size-3.5",
98
+ size: "sm",
99
+ thickness: "1.5"
100
+ })), _cache[7] || (_cache[7] = createElementVNode("span", { class: "max-md:hidden" }, "Pull", -1))]),
101
+ _: 1
102
+ }, 8, [
103
+ "aria-label",
104
+ "disabled",
105
+ "title"
106
+ ]),
107
+ createVNode(unref(ScalarButton), {
108
+ "aria-label": __props.isOffline ? "Push (offline)" : "Push",
109
+ class: "shrink-0 gap-1.5",
110
+ "data-testid": "app-header-push-button",
111
+ disabled: !__props.canPushActiveDocument,
112
+ size: "xs",
113
+ title: __props.isOffline ? "You are offline." : "Push",
114
+ type: "button",
115
+ variant: "solid",
116
+ onClick: _cache[4] || (_cache[4] = ($event) => emit("push"))
117
+ }, {
118
+ default: withCtx(() => [__props.isOffline ? (openBlock(), createBlock(unref(ScalarIconCloudSlash), {
119
+ key: 0,
120
+ class: "size-3.5",
121
+ size: "sm",
122
+ thickness: "1.5"
123
+ })) : (openBlock(), createBlock(unref(ScalarIconCloudArrowUp), {
124
+ key: 1,
125
+ class: "size-3.5",
126
+ size: "sm",
127
+ thickness: "1.5"
128
+ })), _cache[8] || (_cache[8] = createElementVNode("span", { class: "max-md:hidden" }, "Push", -1))]),
129
+ _: 1
130
+ }, 8, [
131
+ "aria-label",
132
+ "disabled",
133
+ "title"
134
+ ])
135
+ ], 64)) : createCommentVNode("", true),
136
+ __props.showTeamPublishAction ? (openBlock(), createBlock(unref(ScalarButton), {
137
+ key: 2,
138
+ "aria-label": __props.isOffline ? "Publish (offline)" : "Publish",
139
+ class: "shrink-0 gap-1.5",
140
+ "data-testid": "app-header-publish-button",
141
+ disabled: __props.isOffline,
142
+ size: "xs",
143
+ title: __props.isOffline ? "You are offline." : "Publish",
144
+ type: "button",
145
+ variant: "solid",
146
+ onClick: _cache[5] || (_cache[5] = ($event) => emit("publish"))
147
+ }, {
148
+ default: withCtx(() => [__props.isOffline ? (openBlock(), createBlock(unref(ScalarIconCloudSlash), {
149
+ key: 0,
150
+ class: "size-3.5",
151
+ size: "sm",
152
+ thickness: "1.5"
153
+ })) : (openBlock(), createBlock(unref(ScalarIconCloudArrowUp), {
154
+ key: 1,
155
+ class: "size-3.5",
156
+ size: "sm",
157
+ thickness: "1.5"
158
+ })), _cache[9] || (_cache[9] = createElementVNode("span", { class: "max-md:hidden" }, "Publish", -1))]),
159
+ _: 1
160
+ }, 8, [
161
+ "aria-label",
162
+ "disabled",
163
+ "title"
164
+ ])) : createCommentVNode("", true)
165
+ ], 64);
166
+ };
167
+ }
168
+ });
169
+ //#endregion
170
+ export { AppHeaderActions_vue_vue_type_script_setup_true_lang_default as default };
171
+
172
+ //# sourceMappingURL=AppHeaderActions.vue.script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AppHeaderActions.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppHeaderActions.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport {\n ScalarIconArrowCounterClockwise,\n ScalarIconCloudArrowDown,\n ScalarIconCloudArrowUp,\n ScalarIconCloudSlash,\n ScalarIconFloppyDisk,\n} from '@scalar/icons'\n\ndefineProps<{\n /** Whether the local-workspace Save / Revert cluster should mount. */\n showLocalSaveActions: boolean\n /** Whether the team-workspace Pull / Push cluster should mount. */\n showTeamSyncActions: boolean\n /** Whether the team-workspace Publish cluster should mount. */\n showTeamPublishAction: boolean\n /** Whether the active document has unsaved local edits. */\n isActiveDocumentDirty: boolean\n /** Whether the browser currently reports as offline. */\n isOffline: boolean\n /** Whether the Pull button is clickable. */\n canPullActiveDocument: boolean\n /** Whether the Push button is clickable. */\n canPushActiveDocument: boolean\n}>()\n\nconst emit = defineEmits<{\n save: []\n revert: []\n pull: []\n push: []\n publish: []\n}>()\n</script>\n\n<template>\n <!--\n Local workspace cluster: Save is always mounted while a document is\n active so the affordance does not jump around, and gets disabled\n when the document is clean. Revert only joins it once there is\n something to revert.\n -->\n <template v-if=\"showLocalSaveActions\">\n <ScalarButton\n v-if=\"isActiveDocumentDirty\"\n aria-label=\"Revert changes\"\n class=\"text-c-2 hover:text-c-1 size-6 shrink-0 p-0\"\n data-testid=\"app-header-revert-button\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"emit('revert')\">\n <ScalarIconArrowCounterClockwise\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n </ScalarButton>\n <ScalarButton\n aria-label=\"Save\"\n class=\"shrink-0 gap-1.5\"\n data-testid=\"app-header-save-button\"\n :disabled=\"!isActiveDocumentDirty\"\n size=\"xs\"\n title=\"Save\"\n type=\"button\"\n variant=\"solid\"\n @click=\"emit('save')\">\n <ScalarIconFloppyDisk\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"max-md:hidden\">Save</span>\n </ScalarButton>\n </template>\n <!--\n Team workspace cluster for registry-backed documents. The same Revert\n affordance as local workspaces sits in front of the Pull / Push pair\n so dirty edits can be discarded without going through the registry.\n Pull / Push enablement tracks the cached `VersionStatus` so only one\n of them is actionable at a time.\n -->\n <template v-if=\"showTeamSyncActions\">\n <ScalarButton\n v-if=\"isActiveDocumentDirty\"\n aria-label=\"Revert changes\"\n class=\"text-c-2 hover:text-c-1 size-6 shrink-0 p-0\"\n data-testid=\"app-header-revert-button\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"emit('revert')\">\n <ScalarIconArrowCounterClockwise\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n </ScalarButton>\n <ScalarButton\n :aria-label=\"isOffline ? 'Pull (offline)' : 'Pull'\"\n class=\"shrink-0 gap-1.5\"\n data-testid=\"app-header-pull-button\"\n :disabled=\"!canPullActiveDocument\"\n size=\"xs\"\n :title=\"isOffline ? 'You are offline.' : 'Pull'\"\n type=\"button\"\n variant=\"solid\"\n @click=\"emit('pull')\">\n <ScalarIconCloudSlash\n v-if=\"isOffline\"\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <ScalarIconCloudArrowDown\n v-else\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"max-md:hidden\">Pull</span>\n </ScalarButton>\n <ScalarButton\n :aria-label=\"isOffline ? 'Push (offline)' : 'Push'\"\n class=\"shrink-0 gap-1.5\"\n data-testid=\"app-header-push-button\"\n :disabled=\"!canPushActiveDocument\"\n size=\"xs\"\n :title=\"isOffline ? 'You are offline.' : 'Push'\"\n type=\"button\"\n variant=\"solid\"\n @click=\"emit('push')\">\n <ScalarIconCloudSlash\n v-if=\"isOffline\"\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <ScalarIconCloudArrowUp\n v-else\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"max-md:hidden\">Push</span>\n </ScalarButton>\n </template>\n <!--\n Team workspace cluster for documents that have not been published\n yet. A single Publish button kicks off the first-time push to the\n registry; once that succeeds the document gets a registry meta and\n switches over to the Pull / Push cluster above on the next render.\n -->\n <ScalarButton\n v-if=\"showTeamPublishAction\"\n :aria-label=\"isOffline ? 'Publish (offline)' : 'Publish'\"\n class=\"shrink-0 gap-1.5\"\n data-testid=\"app-header-publish-button\"\n :disabled=\"isOffline\"\n size=\"xs\"\n :title=\"isOffline ? 'You are offline.' : 'Publish'\"\n type=\"button\"\n variant=\"solid\"\n @click=\"emit('publish')\">\n <ScalarIconCloudSlash\n v-if=\"isOffline\"\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <ScalarIconCloudArrowUp\n v-else\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"max-md:hidden\">Publish</span>\n </ScalarButton>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;EA2BA,MAAM,OAAO;;;IAgBK,QAAA,wBAAA,WAAA,EAAhB,mBA+BW,UAAA,EAAA,KAAA,GAAA,EAAA,CA7BD,QAAA,yBAAA,WAAA,EADR,YAae,MAAA,aAAA,EAAA;;KAXb,cAAW;KACX,OAAM;KACN,eAAY;KACZ,MAAK;KACL,MAAK;KACL,SAAQ;KACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;4BAIQ,CAHpB,YAGoB,MAAA,gCAAA,EAAA;MAFlB,OAAM;MACN,MAAK;MACL,WAAU;;;wCAEd,YAee,MAAA,aAAA,EAAA;KAdb,cAAW;KACX,OAAM;KACN,eAAY;KACX,UAAQ,CAAG,QAAA;KACZ,MAAK;KACL,OAAM;KACN,MAAK;KACL,SAAQ;KACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;4BAIQ,CAHpB,YAGoB,MAAA,qBAAA,EAAA;MAFlB,OAAM;MACN,MAAK;MACL,WAAU;mCACZ,mBAAuC,QAAA,EAAjC,OAAM,iBAAe,EAAC,QAAI,GAAA,EAAA,CAAA;;;IAUpB,QAAA,uBAAA,WAAA,EAAhB,mBA2DW,UAAA,EAAA,KAAA,GAAA,EAAA;KAzDD,QAAA,yBAAA,WAAA,EADR,YAae,MAAA,aAAA,EAAA;;MAXb,cAAW;MACX,OAAM;MACN,eAAY;MACZ,MAAK;MACL,MAAK;MACL,SAAQ;MACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAA;;6BAIQ,CAHpB,YAGoB,MAAA,gCAAA,EAAA;OAFlB,OAAM;OACN,MAAK;OACL,WAAU;;;;KAEd,YAqBe,MAAA,aAAA,EAAA;MApBZ,cAAY,QAAA,YAAS,mBAAA;MACtB,OAAM;MACN,eAAY;MACX,UAAQ,CAAG,QAAA;MACZ,MAAK;MACJ,OAAO,QAAA,YAAS,qBAAA;MACjB,MAAK;MACL,SAAQ;MACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;6BAKQ,CAHZ,QAAA,aAAA,WAAA,EADR,YAIoB,MAAA,qBAAA,EAAA;;OAFlB,OAAM;OACN,MAAK;OACL,WAAU;0BACZ,YAIoB,MAAA,yBAAA,EAAA;;OAFlB,OAAM;OACN,MAAK;OACL,WAAU;qCACZ,mBAAuC,QAAA,EAAjC,OAAM,iBAAe,EAAC,QAAI,GAAA,EAAA,CAAA;;;;;;;KAElC,YAqBe,MAAA,aAAA,EAAA;MApBZ,cAAY,QAAA,YAAS,mBAAA;MACtB,OAAM;MACN,eAAY;MACX,UAAQ,CAAG,QAAA;MACZ,MAAK;MACJ,OAAO,QAAA,YAAS,qBAAA;MACjB,MAAK;MACL,SAAQ;MACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;6BAKQ,CAHZ,QAAA,aAAA,WAAA,EADR,YAIoB,MAAA,qBAAA,EAAA;;OAFlB,OAAM;OACN,MAAK;OACL,WAAU;0BACZ,YAIoB,MAAA,uBAAA,EAAA;;OAFlB,OAAM;OACN,MAAK;OACL,WAAU;qCACZ,mBAAuC,QAAA,EAAjC,OAAM,iBAAe,EAAC,QAAI,GAAA,EAAA,CAAA;;;;;;;;IAU5B,QAAA,yBAAA,WAAA,EADR,YAsBe,MAAA,aAAA,EAAA;;KApBZ,cAAY,QAAA,YAAS,sBAAA;KACtB,OAAM;KACN,eAAY;KACX,UAAU,QAAA;KACX,MAAK;KACJ,OAAO,QAAA,YAAS,qBAAA;KACjB,MAAK;KACL,SAAQ;KACP,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,UAAA;;4BAKQ,CAHZ,QAAA,aAAA,WAAA,EADR,YAIoB,MAAA,qBAAA,EAAA;;MAFlB,OAAM;MACN,MAAK;MACL,WAAU;yBACZ,YAIoB,MAAA,uBAAA,EAAA;;MAFlB,OAAM;MACN,MAAK;MACL,WAAU;oCACZ,mBAA0C,QAAA,EAApC,OAAM,iBAAe,EAAC,WAAO,GAAA,EAAA,CAAA"}
@@ -1,11 +1,8 @@
1
- import type { AppState } from '@scalar/api-client/v2/features/app';
2
- import { type RegistryDocumentsState } from '../../../../v2/features/app/hooks/use-sidebar-documents.js';
3
- import type { ImportDocumentFromRegistry } from '../../../../v2/types/configuration';
1
+ import type { AppState } from '../../../../v2/features/app/index.js';
2
+ import type { ImportDocumentFromRegistry, RegistryDocumentsState } from '../../../../v2/types/configuration';
4
3
  type __VLS_Props = {
5
4
  /** The app state from @scalar/api-client. */
6
5
  app: AppState;
7
- /** Horizontal indent applied to nested sidebar items, in pixels. */
8
- indent?: number;
9
6
  /**
10
7
  * The list of all available registry documents, wrapped in a loading state
11
8
  * so the sidebar can render skeleton placeholders while the registry is
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"AppSidebar.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"names":[],"mappings":"AA6nBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAcjD,OAAO,KAAK,EACV,0BAA0B,EAC1B,sBAAsB,EACvB,MAAM,0BAA0B,CAAA;AAEjC,KAAK,WAAW,GAAG;IACjB,6CAA6C;IAC7C,GAAG,EAAE,QAAQ,CAAA;IACb;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,CAAA;IAC1C,8CAA8C;IAC9C,qBAAqB,CAAC,EAAE,0BAA0B,CAAA;CACnD,CAAC;AA6WF,KAAK,gBAAgB,GAAG;IACxB,wCAAwC;IACxC,cAAc,EAAE,MAAM,CAAC;CACtB,CAAC;AAKF,KAAK,iBAAiB,GAAG,WAAW,GAAG,gBAAgB,CAAC;AAyoBxD,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-1181f741"]]);
5
+ var AppSidebar_default = /* @__PURE__ */ _plugin_vue_export_helper_default(AppSidebar_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-e5f6f69d"]]);
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 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
+ {"version":3,"file":"AppSidebar.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\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 type { AppState } from '@/v2/features/app'\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 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 {\n ImportDocumentFromRegistry,\n RegistryDocumentsState,\n} from '@/v2/types/configuration'\n\nconst {\n app,\n registryDocuments = { status: 'success', documents: [] },\n fetchRegistryDocument,\n} = defineProps<{\n /** The app state from @scalar/api-client. */\n app: AppState\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 }),\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-[calc(var(--app-desktop-tabs-height)+var(--spacing-header,48px)+2.5rem)]\">\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":""}
@@ -10,7 +10,7 @@ import { useSidebarContextMenu } from "../hooks/use-sidebar-context-menu.js";
10
10
  import { useSidebarDocuments } from "../hooks/use-sidebar-documents.js";
11
11
  import { dragHandleFactory } from "../../../helpers/drag-handle-factory.js";
12
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";
13
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, isRef, mergeModels, normalizeClass, onBeforeMount, onBeforeUnmount, openBlock, ref, renderList, renderSlot, unref, useModel, withCtx } from "vue";
14
14
  import { ScalarIconButton, ScalarModal, ScalarSidebar, ScalarSidebarButton, ScalarSidebarItems, ScalarSidebarSearchInput, ScalarSidebarSection, useModal } from "@scalar/components";
15
15
  import { ScalarIconFolderDashed, ScalarIconFunnel, ScalarIconGearSix, ScalarIconPlus } from "@scalar/icons";
16
16
  import { useToasts } from "@scalar/use-toasts";
@@ -31,7 +31,6 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
31
31
  __name: "AppSidebar",
32
32
  props: /* @__PURE__ */ mergeModels({
33
33
  app: {},
34
- indent: { default: 20 },
35
34
  registryDocuments: { default: () => ({
36
35
  status: "success",
37
36
  documents: []
@@ -102,8 +101,7 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
102
101
  workspaceStore,
103
102
  namespace: registry.namespace,
104
103
  slug: registry.slug,
105
- version: targetVersion?.version,
106
- commitHash: targetVersion?.registryCommitHash
104
+ version: targetVersion?.version
107
105
  }));
108
106
  loadingKeys.value[item.key] = false;
109
107
  if (!outcome.ok) {
@@ -293,10 +291,7 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
293
291
  }])
294
292
  }, {
295
293
  default: withCtx(() => [
296
- createElementVNode("div", _hoisted_1, [createVNode(unref(ScalarSidebar), {
297
- class: "flex min-h-0 flex-1 flex-col max-md:pt-12",
298
- style: normalizeStyle({ "--scalar-sidebar-indent": __props.indent + "px" })
299
- }, {
294
+ createElementVNode("div", _hoisted_1, [createVNode(unref(ScalarSidebar), { class: "flex min-h-0 flex-1 flex-col max-md:pt-[calc(var(--app-desktop-tabs-height)+var(--spacing-header,48px)+2.5rem)]" }, {
300
295
  default: withCtx(() => [
301
296
  !isOnDocumentPage.value ? (openBlock(), createElementBlock("div", _hoisted_2, [createElementVNode("div", _hoisted_3, [
302
297
  createVNode(unref(ScalarSidebarButton), {
@@ -418,7 +413,7 @@ var AppSidebar_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ def
418
413
  renderSlot(_ctx.$slots, "footer", {}, void 0, true)
419
414
  ]),
420
415
  _: 3
421
- }, 8, ["style"])]),
416
+ })]),
422
417
  createVNode(unref(DocumentSearchModal_default), {
423
418
  document: activeDocument.value,
424
419
  modalState: unref(searchModal),
@@ -1 +1 @@
1
- {"version":3,"file":"AppSidebar.vue.script.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":"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"}
1
+ {"version":3,"file":"AppSidebar.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/AppSidebar.vue"],"sourcesContent":["<script setup lang=\"ts\">\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 type { AppState } from '@/v2/features/app'\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 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 {\n ImportDocumentFromRegistry,\n RegistryDocumentsState,\n} from '@/v2/types/configuration'\n\nconst {\n app,\n registryDocuments = { status: 'success', documents: [] },\n fetchRegistryDocument,\n} = defineProps<{\n /** The app state from @scalar/api-client. */\n app: AppState\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 }),\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-[calc(var(--app-desktop-tabs-height)+var(--spacing-header,48px)+2.5rem)]\">\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":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DesktopTabs.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/DesktopTabs.vue"],"names":[],"mappings":"AAgFA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AACvE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oEAAoE,CAAA;AAK7F,KAAK,WAAW,GAAG;IACjB,iBAAiB;IACjB,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,QAAQ,EAAE,iBAAiB,CAAA;CAC5B,CAAC;AA+JF,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"DesktopTabs.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/DesktopTabs.vue"],"names":[],"mappings":"AAgFA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AACvE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,oEAAoE,CAAA;AAK7F,KAAK,WAAW,GAAG;IACjB,iBAAiB;IACjB,IAAI,EAAE,GAAG,EAAE,CAAA;IACX,+BAA+B;IAC/B,cAAc,EAAE,MAAM,CAAA;IACtB,0DAA0D;IAC1D,QAAQ,EAAE,iBAAiB,CAAA;CAC5B,CAAC;AAgKF,QAAA,MAAM,YAAY,kSAEhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
@@ -1 +1 @@
1
- {"version":3,"file":"DesktopTabs.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/DesktopTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon } from '@scalar/components'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { Tab } from '@scalar/workspace-store/schemas/extensions/workspace/x-scalar-tabs'\nimport { computed } from 'vue'\n\nimport DesktopTab from '@/v2/features/app/components/DesktopTab.vue'\n\nconst { tabs, eventBus } = defineProps<{\n /** Tabs state */\n tabs: Tab[]\n /** Current active tab index */\n activeTabIndex: number\n /** Workspace event bus for emitting tab-related events */\n eventBus: WorkspaceEventBus\n}>()\n\n/** Whether there is only a single tab open */\nconst isSingleTab = computed((): boolean => tabs.length === 1)\n\n/** Adds a new tab */\nconst handleAddTab = (): void => {\n eventBus.emit('tabs:add:tab', undefined)\n}\n\n/** Switches to the tab at the specified index */\nconst switchTab = (index: number): void => {\n eventBus.emit('tabs:focus:tab', { index })\n}\n\n/** Closes the tab at the specified index */\nconst handleCloseTab = (index: number): void => {\n eventBus.emit('tabs:close:tab', { index })\n}\n\n/** Closes all tabs except the one at the specified index */\nconst handleCloseOtherTabs = (index: number): void => {\n eventBus.emit('tabs:close:other-tabs', { index })\n}\n\nconst handleCopyTabUrl = (index: number): void => {\n eventBus.emit('tabs:copy:url', { index })\n}\n</script>\n\n<template>\n <nav class=\"t-app__top-nav\">\n <!-- drag region (macos) -->\n <div class=\"mac:app-drag-region\">\n <div class=\"z-1 flex h-10 items-center gap-2 px-2\">\n <DesktopTab\n v-for=\"(tab, index) in tabs\"\n :key=\"index\"\n :active=\"index === activeTabIndex\"\n :hotkey=\"!isSingleTab && index < 9 ? String(index + 1) : undefined\"\n :isSingleTab=\"isSingleTab\"\n :tab=\"tab\"\n @click=\"switchTab(index)\"\n @close=\"handleCloseTab(index)\"\n @closeOtherTabs=\"handleCloseOtherTabs(index)\"\n @copyUrl=\"() => handleCopyTabUrl(index)\"\n @newTab=\"handleAddTab\" />\n\n <button\n class=\"text-c-3 hover:bg-b-3 app-no-drag-region rounded p-1.5\"\n type=\"button\"\n @click=\"handleAddTab\">\n <ScalarIcon\n icon=\"Add\"\n size=\"sm\"\n thickness=\"2.5\" />\n </button>\n </div>\n </div>\n </nav>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"DesktopTabs.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/DesktopTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon } from '@scalar/components'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { Tab } from '@scalar/workspace-store/schemas/extensions/workspace/x-scalar-tabs'\nimport { computed } from 'vue'\n\nimport DesktopTab from '@/v2/features/app/components/DesktopTab.vue'\n\nconst { tabs, eventBus } = defineProps<{\n /** Tabs state */\n tabs: Tab[]\n /** Current active tab index */\n activeTabIndex: number\n /** Workspace event bus for emitting tab-related events */\n eventBus: WorkspaceEventBus\n}>()\n\n/** Whether there is only a single tab open */\nconst isSingleTab = computed((): boolean => tabs.length === 1)\n\n/** Adds a new tab */\nconst handleAddTab = (): void => {\n eventBus.emit('tabs:add:tab', undefined)\n}\n\n/** Switches to the tab at the specified index */\nconst switchTab = (index: number): void => {\n eventBus.emit('tabs:focus:tab', { index })\n}\n\n/** Closes the tab at the specified index */\nconst handleCloseTab = (index: number): void => {\n eventBus.emit('tabs:close:tab', { index })\n}\n\n/** Closes all tabs except the one at the specified index */\nconst handleCloseOtherTabs = (index: number): void => {\n eventBus.emit('tabs:close:other-tabs', { index })\n}\n\nconst handleCopyTabUrl = (index: number): void => {\n eventBus.emit('tabs:copy:url', { index })\n}\n</script>\n\n<template>\n <nav class=\"z-10\">\n <!-- drag region (macos) -->\n <div class=\"mac:app-drag-region mac:pl-[72px]\">\n <div class=\"z-1 flex h-10 items-center gap-2 px-2\">\n <DesktopTab\n v-for=\"(tab, index) in tabs\"\n :key=\"index\"\n :active=\"index === activeTabIndex\"\n :hotkey=\"!isSingleTab && index < 9 ? String(index + 1) : undefined\"\n :isSingleTab=\"isSingleTab\"\n :tab=\"tab\"\n @click=\"switchTab(index)\"\n @close=\"handleCloseTab(index)\"\n @closeOtherTabs=\"handleCloseOtherTabs(index)\"\n @copyUrl=\"() => handleCopyTabUrl(index)\"\n @newTab=\"handleAddTab\" />\n\n <button\n class=\"text-c-3 hover:bg-b-3 app-no-drag-region rounded p-1.5\"\n type=\"button\"\n @click=\"handleAddTab\">\n <ScalarIcon\n icon=\"Add\"\n size=\"sm\"\n thickness=\"2.5\" />\n </button>\n </div>\n </div>\n </nav>\n</template>\n"],"mappings":""}
@@ -2,8 +2,8 @@ import DesktopTab_default from "./DesktopTab.vue.js";
2
2
  import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, openBlock, renderList, unref } from "vue";
3
3
  import { ScalarIcon } from "@scalar/components";
4
4
  //#region src/v2/features/app/components/DesktopTabs.vue?vue&type=script&setup=true&lang.ts
5
- var _hoisted_1 = { class: "t-app__top-nav" };
6
- var _hoisted_2 = { class: "mac:app-drag-region" };
5
+ var _hoisted_1 = { class: "z-10" };
6
+ var _hoisted_2 = { class: "mac:app-drag-region mac:pl-[72px]" };
7
7
  var _hoisted_3 = { class: "z-1 flex h-10 items-center gap-2 px-2" };
8
8
  var DesktopTabs_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
9
9
  __name: "DesktopTabs",
@@ -1 +1 @@
1
- {"version":3,"file":"DesktopTabs.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/DesktopTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon } from '@scalar/components'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { Tab } from '@scalar/workspace-store/schemas/extensions/workspace/x-scalar-tabs'\nimport { computed } from 'vue'\n\nimport DesktopTab from '@/v2/features/app/components/DesktopTab.vue'\n\nconst { tabs, eventBus } = defineProps<{\n /** Tabs state */\n tabs: Tab[]\n /** Current active tab index */\n activeTabIndex: number\n /** Workspace event bus for emitting tab-related events */\n eventBus: WorkspaceEventBus\n}>()\n\n/** Whether there is only a single tab open */\nconst isSingleTab = computed((): boolean => tabs.length === 1)\n\n/** Adds a new tab */\nconst handleAddTab = (): void => {\n eventBus.emit('tabs:add:tab', undefined)\n}\n\n/** Switches to the tab at the specified index */\nconst switchTab = (index: number): void => {\n eventBus.emit('tabs:focus:tab', { index })\n}\n\n/** Closes the tab at the specified index */\nconst handleCloseTab = (index: number): void => {\n eventBus.emit('tabs:close:tab', { index })\n}\n\n/** Closes all tabs except the one at the specified index */\nconst handleCloseOtherTabs = (index: number): void => {\n eventBus.emit('tabs:close:other-tabs', { index })\n}\n\nconst handleCopyTabUrl = (index: number): void => {\n eventBus.emit('tabs:copy:url', { index })\n}\n</script>\n\n<template>\n <nav class=\"t-app__top-nav\">\n <!-- drag region (macos) -->\n <div class=\"mac:app-drag-region\">\n <div class=\"z-1 flex h-10 items-center gap-2 px-2\">\n <DesktopTab\n v-for=\"(tab, index) in tabs\"\n :key=\"index\"\n :active=\"index === activeTabIndex\"\n :hotkey=\"!isSingleTab && index < 9 ? String(index + 1) : undefined\"\n :isSingleTab=\"isSingleTab\"\n :tab=\"tab\"\n @click=\"switchTab(index)\"\n @close=\"handleCloseTab(index)\"\n @closeOtherTabs=\"handleCloseOtherTabs(index)\"\n @copyUrl=\"() => handleCopyTabUrl(index)\"\n @newTab=\"handleAddTab\" />\n\n <button\n class=\"text-c-3 hover:bg-b-3 app-no-drag-region rounded p-1.5\"\n type=\"button\"\n @click=\"handleAddTab\">\n <ScalarIcon\n icon=\"Add\"\n size=\"sm\"\n thickness=\"2.5\" />\n </button>\n </div>\n </div>\n </nav>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAkBA,MAAM,cAAc,eAAwB,QAAA,KAAK,WAAW,EAAC;;EAG7D,MAAM,qBAA2B;AAC/B,WAAA,SAAS,KAAK,gBAAgB,KAAA,EAAS;;;EAIzC,MAAM,aAAa,UAAwB;AACzC,WAAA,SAAS,KAAK,kBAAkB,EAAE,OAAO,CAAA;;;EAI3C,MAAM,kBAAkB,UAAwB;AAC9C,WAAA,SAAS,KAAK,kBAAkB,EAAE,OAAO,CAAA;;;EAI3C,MAAM,wBAAwB,UAAwB;AACpD,WAAA,SAAS,KAAK,yBAAyB,EAAE,OAAO,CAAA;;EAGlD,MAAM,oBAAoB,UAAwB;AAChD,WAAA,SAAS,KAAK,iBAAiB,EAAE,OAAO,CAAA;;;uBAKxC,mBA4BM,OA5BN,YA4BM,CA1BJ,mBAyBM,OAzBN,YAyBM,CAxBJ,mBAuBM,OAvBN,YAuBM,EAAA,UAAA,KAAA,EAtBJ,mBAW2B,UAAA,MAAA,WAVF,QAAA,OAAf,KAAK,UAAK;wBADpB,YAW2B,oBAAA;KATxB,KAAK;KACL,QAAQ,UAAU,QAAA;KAClB,QAAM,CAAG,YAAA,SAAe,QAAK,IAAO,OAAO,QAAK,EAAA,GAAQ,KAAA;KACxD,aAAa,YAAA;KACR;KACL,UAAK,WAAE,UAAU,MAAK;KACtB,UAAK,WAAE,eAAe,MAAK;KAC3B,mBAAc,WAAE,qBAAqB,MAAK;KAC1C,iBAAe,iBAAiB,MAAK;KACrC,UAAQ;;;;;;;;;;;cAEX,mBAQS,UAAA;IAPP,OAAM;IACN,MAAK;IACJ,SAAO;OACR,YAGoB,MAAA,WAAA,EAAA;IAFlB,MAAK;IACL,MAAK;IACL,WAAU"}
1
+ {"version":3,"file":"DesktopTabs.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/DesktopTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarIcon } from '@scalar/components'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport type { Tab } from '@scalar/workspace-store/schemas/extensions/workspace/x-scalar-tabs'\nimport { computed } from 'vue'\n\nimport DesktopTab from '@/v2/features/app/components/DesktopTab.vue'\n\nconst { tabs, eventBus } = defineProps<{\n /** Tabs state */\n tabs: Tab[]\n /** Current active tab index */\n activeTabIndex: number\n /** Workspace event bus for emitting tab-related events */\n eventBus: WorkspaceEventBus\n}>()\n\n/** Whether there is only a single tab open */\nconst isSingleTab = computed((): boolean => tabs.length === 1)\n\n/** Adds a new tab */\nconst handleAddTab = (): void => {\n eventBus.emit('tabs:add:tab', undefined)\n}\n\n/** Switches to the tab at the specified index */\nconst switchTab = (index: number): void => {\n eventBus.emit('tabs:focus:tab', { index })\n}\n\n/** Closes the tab at the specified index */\nconst handleCloseTab = (index: number): void => {\n eventBus.emit('tabs:close:tab', { index })\n}\n\n/** Closes all tabs except the one at the specified index */\nconst handleCloseOtherTabs = (index: number): void => {\n eventBus.emit('tabs:close:other-tabs', { index })\n}\n\nconst handleCopyTabUrl = (index: number): void => {\n eventBus.emit('tabs:copy:url', { index })\n}\n</script>\n\n<template>\n <nav class=\"z-10\">\n <!-- drag region (macos) -->\n <div class=\"mac:app-drag-region mac:pl-[72px]\">\n <div class=\"z-1 flex h-10 items-center gap-2 px-2\">\n <DesktopTab\n v-for=\"(tab, index) in tabs\"\n :key=\"index\"\n :active=\"index === activeTabIndex\"\n :hotkey=\"!isSingleTab && index < 9 ? String(index + 1) : undefined\"\n :isSingleTab=\"isSingleTab\"\n :tab=\"tab\"\n @click=\"switchTab(index)\"\n @close=\"handleCloseTab(index)\"\n @closeOtherTabs=\"handleCloseOtherTabs(index)\"\n @copyUrl=\"() => handleCopyTabUrl(index)\"\n @newTab=\"handleAddTab\" />\n\n <button\n class=\"text-c-3 hover:bg-b-3 app-no-drag-region rounded p-1.5\"\n type=\"button\"\n @click=\"handleAddTab\">\n <ScalarIcon\n icon=\"Add\"\n size=\"sm\"\n thickness=\"2.5\" />\n </button>\n </div>\n </div>\n </nav>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;EAkBA,MAAM,cAAc,eAAwB,QAAA,KAAK,WAAW,EAAC;;EAG7D,MAAM,qBAA2B;AAC/B,WAAA,SAAS,KAAK,gBAAgB,KAAA,EAAS;;;EAIzC,MAAM,aAAa,UAAwB;AACzC,WAAA,SAAS,KAAK,kBAAkB,EAAE,OAAO,CAAA;;;EAI3C,MAAM,kBAAkB,UAAwB;AAC9C,WAAA,SAAS,KAAK,kBAAkB,EAAE,OAAO,CAAA;;;EAI3C,MAAM,wBAAwB,UAAwB;AACpD,WAAA,SAAS,KAAK,yBAAyB,EAAE,OAAO,CAAA;;EAGlD,MAAM,oBAAoB,UAAwB;AAChD,WAAA,SAAS,KAAK,iBAAiB,EAAE,OAAO,CAAA;;;uBAKxC,mBA4BM,OA5BN,YA4BM,CA1BJ,mBAyBM,OAzBN,YAyBM,CAxBJ,mBAuBM,OAvBN,YAuBM,EAAA,UAAA,KAAA,EAtBJ,mBAW2B,UAAA,MAAA,WAVF,QAAA,OAAf,KAAK,UAAK;wBADpB,YAW2B,oBAAA;KATxB,KAAK;KACL,QAAQ,UAAU,QAAA;KAClB,QAAM,CAAG,YAAA,SAAe,QAAK,IAAO,OAAO,QAAK,EAAA,GAAQ,KAAA;KACxD,aAAa,YAAA;KACR;KACL,UAAK,WAAE,UAAU,MAAK;KACtB,UAAK,WAAE,eAAe,MAAK;KAC3B,mBAAc,WAAE,qBAAqB,MAAK;KAC1C,iBAAe,iBAAiB,MAAK;KACrC,UAAQ;;;;;;;;;;;cAEX,mBAQS,UAAA;IAPP,OAAM;IACN,MAAK;IACJ,SAAO;OACR,YAGoB,MAAA,WAAA,EAAA;IAFlB,MAAK;IACL,MAAK;IACL,WAAU"}
@@ -1,6 +1,5 @@
1
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
- import type { ImportDocumentFromRegistry } from '../../../../v2/types/configuration';
2
+ import type { ImportDocumentFromRegistry, RegistryDocumentsState } from '../../../../v2/types/configuration';
4
3
  type __VLS_Props = {
5
4
  /** The app state used to read the active document and emit navigation events. */
6
5
  app: AppState;