@scalar/api-client 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/style.css +3917 -4765
- package/dist/styles/tailwind.config.css +20 -0
- package/dist/styles/utilities.css +45 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +1 -0
- package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
- package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.d.ts.map +1 -1
- package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js +1 -1
- package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js.map +1 -1
- package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js +1 -1
- package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js.map +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.d.ts +1 -1
- package/dist/v2/components/data-table/DataTableInput.vue.d.ts.map +1 -1
- package/dist/v2/constants.js +1 -1
- package/dist/v2/features/app/App.vue.d.ts +15 -31
- package/dist/v2/features/app/App.vue.d.ts.map +1 -1
- package/dist/v2/features/app/App.vue.js.map +1 -1
- package/dist/v2/features/app/App.vue.script.js +107 -28
- package/dist/v2/features/app/App.vue.script.js.map +1 -1
- package/dist/v2/features/app/app-events.js +1 -1
- package/dist/v2/features/app/app-events.js.map +1 -1
- package/dist/v2/features/app/app-state.d.ts +10 -14
- package/dist/v2/features/app/app-state.d.ts.map +1 -1
- package/dist/v2/features/app/app-state.js +54 -22
- package/dist/v2/features/app/app-state.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.script.js +1 -1
- package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts +32 -0
- package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/AppHeaderActions.vue.js +7 -0
- package/dist/v2/features/app/components/AppHeaderActions.vue.js.map +1 -0
- package/dist/v2/features/app/components/AppHeaderActions.vue.script.js +170 -0
- package/dist/v2/features/app/components/AppHeaderActions.vue.script.js.map +1 -0
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts +2 -3
- package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
- package/dist/v2/features/app/components/AppSidebar.vue.script.js +1 -2
- package/dist/v2/features/app/components/AppSidebar.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts +1 -2
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.js.map +1 -1
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js +3 -34
- package/dist/v2/features/app/components/DocumentBreadcrumb.vue.script.js.map +1 -1
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts +1 -1
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.d.ts.map +1 -1
- package/dist/v2/features/app/components/PublishDocumentModal.vue.d.ts +77 -0
- package/dist/v2/features/app/components/PublishDocumentModal.vue.d.ts.map +1 -0
- package/dist/v2/features/app/components/PublishDocumentModal.vue.js +7 -0
- package/dist/v2/features/app/components/PublishDocumentModal.vue.js.map +1 -0
- package/dist/v2/features/app/components/PublishDocumentModal.vue.script.js +209 -0
- package/dist/v2/features/app/components/PublishDocumentModal.vue.script.js.map +1 -0
- package/dist/v2/features/app/components/SyncConflictResolutionEditor.vue.d.ts.map +1 -0
- package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.js +2 -2
- package/dist/v2/features/app/components/SyncConflictResolutionEditor.vue.js.map +1 -0
- package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.script.js +1 -1
- package/dist/v2/features/{collection/components/SyncConflictResolutionEditor.vue.js.map → app/components/SyncConflictResolutionEditor.vue.script.js.map} +1 -1
- package/dist/v2/features/app/helpers/check-version-conflict.d.ts +8 -5
- package/dist/v2/features/app/helpers/check-version-conflict.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/check-version-conflict.js +10 -7
- package/dist/v2/features/app/helpers/check-version-conflict.js.map +1 -1
- package/dist/v2/features/app/helpers/create-api-client-app.d.ts +8 -5
- package/dist/v2/features/app/helpers/create-api-client-app.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/create-api-client-app.js +2 -2
- package/dist/v2/features/app/helpers/create-api-client-app.js.map +1 -1
- package/dist/v2/features/app/helpers/load-registry-document.d.ts +1 -10
- package/dist/v2/features/app/helpers/load-registry-document.d.ts.map +1 -1
- package/dist/v2/features/app/helpers/load-registry-document.js +6 -5
- package/dist/v2/features/app/helpers/load-registry-document.js.map +1 -1
- package/dist/v2/features/app/helpers/registry-error-messages.d.ts +23 -0
- package/dist/v2/features/app/helpers/registry-error-messages.d.ts.map +1 -0
- package/dist/v2/features/app/helpers/registry-error-messages.js +63 -0
- package/dist/v2/features/app/helpers/registry-error-messages.js.map +1 -0
- package/dist/v2/features/app/hooks/use-active-document-version.d.ts +2 -1
- package/dist/v2/features/app/hooks/use-active-document-version.d.ts.map +1 -1
- package/dist/v2/features/app/hooks/use-active-document-version.js.map +1 -1
- package/dist/v2/features/app/hooks/use-document-sync.d.ts +126 -0
- package/dist/v2/features/app/hooks/use-document-sync.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-document-sync.js +448 -0
- package/dist/v2/features/app/hooks/use-document-sync.js.map +1 -0
- package/dist/v2/features/app/hooks/use-network-status.d.ts +29 -0
- package/dist/v2/features/app/hooks/use-network-status.d.ts.map +1 -0
- package/dist/v2/features/app/hooks/use-network-status.js +58 -0
- package/dist/v2/features/app/hooks/use-network-status.js.map +1 -0
- package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts +1 -25
- package/dist/v2/features/app/hooks/use-sidebar-documents.d.ts.map +1 -1
- package/dist/v2/features/app/hooks/use-sidebar-documents.js.map +1 -1
- package/dist/v2/features/app/index.d.ts +1 -1
- package/dist/v2/features/app/index.d.ts.map +1 -1
- package/dist/v2/features/collection/DocumentCollection.vue.d.ts.map +1 -1
- package/dist/v2/features/collection/DocumentCollection.vue.js.map +1 -1
- package/dist/v2/features/collection/DocumentCollection.vue.script.js +43 -277
- package/dist/v2/features/collection/DocumentCollection.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.d.ts.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.js.map +1 -1
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js +25 -9
- package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js.map +1 -1
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.d.ts.map +1 -1
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.js +2 -2
- package/dist/v2/features/command-palette/helpers/generate-unique-slug.js.map +1 -1
- package/dist/v2/features/editor/hooks/use-three-way-merge-editor.d.ts.map +1 -1
- package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js +5 -5
- package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js.map +1 -1
- package/dist/v2/types/configuration.d.ts +273 -7
- package/dist/v2/types/configuration.d.ts.map +1 -1
- package/dist/vue-styles.css +1389 -0
- package/package.json +22 -15
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js +0 -7
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js.map +0 -1
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js +0 -51
- package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js.map +0 -1
- package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.d.ts.map +0 -1
- package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.script.js.map +0 -1
- package/dist/v2/helpers/slugify.d.ts +0 -9
- package/dist/v2/helpers/slugify.d.ts.map +0 -1
- package/dist/v2/helpers/slugify.js +0 -15
- package/dist/v2/helpers/slugify.js.map +0 -1
- /package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.d.ts +0 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, openBlock, ref, toDisplayString, unref, vModelText, watch, withCtx, withDirectives, withModifiers } from "vue";
|
|
2
|
+
import { ScalarButton, ScalarListbox, ScalarModal, useLoadingState } from "@scalar/components";
|
|
3
|
+
import { ScalarIconBuildings, ScalarIconCaretDown } from "@scalar/icons";
|
|
4
|
+
import { slugify } from "@scalar/helpers/string/slugify";
|
|
5
|
+
//#region src/v2/features/app/components/PublishDocumentModal.vue?vue&type=script&setup=true&lang.ts
|
|
6
|
+
var _hoisted_1 = { class: "flex flex-col gap-1.5" };
|
|
7
|
+
var _hoisted_2 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "border-border bg-b-2 flex h-8 items-center gap-2 rounded border px-3 text-sm"
|
|
10
|
+
};
|
|
11
|
+
var _hoisted_3 = { class: "flex min-w-0 items-center gap-2" };
|
|
12
|
+
var _hoisted_4 = { class: "truncate" };
|
|
13
|
+
var _hoisted_5 = ["title"];
|
|
14
|
+
var _hoisted_6 = { class: "truncate" };
|
|
15
|
+
var _hoisted_7 = { class: "flex flex-col gap-1.5" };
|
|
16
|
+
var _hoisted_8 = { class: "flex flex-col gap-1.5" };
|
|
17
|
+
var _hoisted_9 = {
|
|
18
|
+
key: 0,
|
|
19
|
+
class: "text-c-2 -mt-1 text-xs"
|
|
20
|
+
};
|
|
21
|
+
var _hoisted_10 = { class: "text-c-1 font-mono" };
|
|
22
|
+
var _hoisted_11 = {
|
|
23
|
+
key: 1,
|
|
24
|
+
class: "text-red bg-b-2 rounded p-2 text-xs",
|
|
25
|
+
role: "alert"
|
|
26
|
+
};
|
|
27
|
+
var _hoisted_12 = { class: "flex justify-end gap-2 pt-1" };
|
|
28
|
+
var PublishDocumentModal_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
|
|
29
|
+
__name: "PublishDocumentModal",
|
|
30
|
+
props: {
|
|
31
|
+
state: {},
|
|
32
|
+
namespaces: {},
|
|
33
|
+
defaultSlug: { default: "" },
|
|
34
|
+
defaultVersion: { default: "1.0.0" }
|
|
35
|
+
},
|
|
36
|
+
emits: ["submit"],
|
|
37
|
+
setup(__props, { emit: __emit }) {
|
|
38
|
+
const emit = __emit;
|
|
39
|
+
const namespaceOptions = computed(() => (__props.namespaces.status === "success" ? __props.namespaces.namespaces : __props.namespaces.namespaces ?? []).map((entry) => ({
|
|
40
|
+
id: entry.namespace,
|
|
41
|
+
label: entry.title?.trim() || entry.namespace
|
|
42
|
+
})));
|
|
43
|
+
const isNamespacesLoading = computed(() => __props.namespaces.status === "loading");
|
|
44
|
+
/**
|
|
45
|
+
* The picker component is only rendered when there is more than one
|
|
46
|
+
* namespace to choose between. Single-namespace setups read as a static
|
|
47
|
+
* label so the user does not have to interact with a one-item dropdown.
|
|
48
|
+
*/
|
|
49
|
+
const hasMultipleNamespaces = computed(() => namespaceOptions.value.length > 1);
|
|
50
|
+
const selectedNamespace = ref(void 0);
|
|
51
|
+
const slug = ref("");
|
|
52
|
+
const version = ref("");
|
|
53
|
+
const errorMessage = ref(null);
|
|
54
|
+
const loader = useLoadingState();
|
|
55
|
+
/**
|
|
56
|
+
* Resets the form whenever the modal opens. Keeping this scoped to the
|
|
57
|
+
* `open` flag (and not to every prop change) preserves user input
|
|
58
|
+
* across reactive updates while the modal is already on screen.
|
|
59
|
+
*/
|
|
60
|
+
watch(() => __props.state.open, (isOpen) => {
|
|
61
|
+
if (!isOpen) return;
|
|
62
|
+
slug.value = slugify(__props.defaultSlug);
|
|
63
|
+
version.value = __props.defaultVersion;
|
|
64
|
+
errorMessage.value = null;
|
|
65
|
+
selectedNamespace.value = namespaceOptions.value[0];
|
|
66
|
+
loader.clear();
|
|
67
|
+
});
|
|
68
|
+
/** Keep the listbox selection in sync when the namespace list arrives. */
|
|
69
|
+
watch(namespaceOptions, (options) => {
|
|
70
|
+
if (selectedNamespace.value) return;
|
|
71
|
+
selectedNamespace.value = options[0];
|
|
72
|
+
});
|
|
73
|
+
const trimmedSlug = computed(() => slug.value.trim());
|
|
74
|
+
const trimmedVersion = computed(() => version.value.trim());
|
|
75
|
+
/**
|
|
76
|
+
* Human-friendly preview of the resulting registry coordinate
|
|
77
|
+
* (`namespace/slug@version`). Returns `null` while any of the three
|
|
78
|
+
* pieces is still missing so we never show a half-rendered path.
|
|
79
|
+
* Mirrors the same fields submitted to the publish call so the user
|
|
80
|
+
* sees exactly what will land on the registry.
|
|
81
|
+
*/
|
|
82
|
+
const publishPreview = computed(() => {
|
|
83
|
+
const namespace = selectedNamespace.value?.id;
|
|
84
|
+
if (!namespace || !trimmedSlug.value || !trimmedVersion.value) return null;
|
|
85
|
+
return `${namespace}/${trimmedSlug.value}@${trimmedVersion.value}`;
|
|
86
|
+
});
|
|
87
|
+
const isSubmitDisabled = computed(() => !selectedNamespace.value || trimmedSlug.value.length === 0 || trimmedVersion.value.length === 0 || loader.isLoading);
|
|
88
|
+
const handleSubmit = () => {
|
|
89
|
+
errorMessage.value = null;
|
|
90
|
+
const namespace = selectedNamespace.value?.id;
|
|
91
|
+
if (!namespace || !trimmedSlug.value || !trimmedVersion.value) return;
|
|
92
|
+
loader.start();
|
|
93
|
+
emit("submit", {
|
|
94
|
+
input: {
|
|
95
|
+
namespace: String(namespace),
|
|
96
|
+
slug: trimmedSlug.value,
|
|
97
|
+
version: trimmedVersion.value
|
|
98
|
+
},
|
|
99
|
+
done: (outcome) => {
|
|
100
|
+
if (outcome.ok) {
|
|
101
|
+
loader.clear();
|
|
102
|
+
__props.state.hide();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
errorMessage.value = outcome.message;
|
|
106
|
+
loader.invalidate();
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
return (_ctx, _cache) => {
|
|
111
|
+
return openBlock(), createBlock(unref(ScalarModal), {
|
|
112
|
+
size: "sm",
|
|
113
|
+
state: __props.state,
|
|
114
|
+
title: "Publish to registry",
|
|
115
|
+
variant: "form"
|
|
116
|
+
}, {
|
|
117
|
+
default: withCtx(() => [createElementVNode("form", {
|
|
118
|
+
class: "flex flex-col gap-4",
|
|
119
|
+
onSubmit: withModifiers(handleSubmit, ["prevent"])
|
|
120
|
+
}, [
|
|
121
|
+
createElementVNode("div", _hoisted_1, [_cache[5] || (_cache[5] = createElementVNode("label", { class: "text-c-1 text-xs font-medium" }, "Namespace", -1)), isNamespacesLoading.value && namespaceOptions.value.length === 0 ? (openBlock(), createElementBlock("div", _hoisted_2, [createVNode(unref(ScalarIconBuildings), {
|
|
122
|
+
class: "text-c-3 size-3.5 shrink-0",
|
|
123
|
+
size: "sm",
|
|
124
|
+
thickness: "1.5"
|
|
125
|
+
}), _cache[4] || (_cache[4] = createElementVNode("span", { class: "text-c-3" }, "Loading namespaces…", -1))])) : hasMultipleNamespaces.value ? (openBlock(), createBlock(unref(ScalarListbox), {
|
|
126
|
+
key: 1,
|
|
127
|
+
modelValue: selectedNamespace.value,
|
|
128
|
+
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedNamespace.value = $event),
|
|
129
|
+
options: namespaceOptions.value,
|
|
130
|
+
teleport: ""
|
|
131
|
+
}, {
|
|
132
|
+
default: withCtx(() => [createVNode(unref(ScalarButton), {
|
|
133
|
+
class: "border-border text-c-1 hover:bg-b-2 flex h-8 w-full items-center justify-between gap-2 rounded border px-3 font-normal",
|
|
134
|
+
fullWidth: "",
|
|
135
|
+
type: "button",
|
|
136
|
+
variant: "outlined"
|
|
137
|
+
}, {
|
|
138
|
+
default: withCtx(() => [createElementVNode("span", _hoisted_3, [createVNode(unref(ScalarIconBuildings), {
|
|
139
|
+
class: "text-c-2 size-3.5 shrink-0",
|
|
140
|
+
size: "sm",
|
|
141
|
+
thickness: "1.5"
|
|
142
|
+
}), createElementVNode("span", _hoisted_4, toDisplayString(selectedNamespace.value?.label ?? "Select a namespace"), 1)]), createVNode(unref(ScalarIconCaretDown), {
|
|
143
|
+
class: "text-c-2 size-3.5 shrink-0",
|
|
144
|
+
size: "sm",
|
|
145
|
+
thickness: "1.5"
|
|
146
|
+
})]),
|
|
147
|
+
_: 1
|
|
148
|
+
})]),
|
|
149
|
+
_: 1
|
|
150
|
+
}, 8, ["modelValue", "options"])) : (openBlock(), createElementBlock("div", {
|
|
151
|
+
key: 2,
|
|
152
|
+
class: "border-border bg-b-2 text-c-1 flex h-8 items-center gap-2 rounded border px-3 text-sm",
|
|
153
|
+
title: selectedNamespace.value ? `Publishing to ${selectedNamespace.value.label}` : void 0
|
|
154
|
+
}, [createVNode(unref(ScalarIconBuildings), {
|
|
155
|
+
class: "text-c-2 size-3.5 shrink-0",
|
|
156
|
+
size: "sm",
|
|
157
|
+
thickness: "1.5"
|
|
158
|
+
}), createElementVNode("span", _hoisted_6, toDisplayString(selectedNamespace.value?.label ?? "—"), 1)], 8, _hoisted_5))]),
|
|
159
|
+
createElementVNode("div", _hoisted_7, [_cache[6] || (_cache[6] = createElementVNode("label", {
|
|
160
|
+
class: "text-c-1 text-xs font-medium",
|
|
161
|
+
for: "publish-document-slug"
|
|
162
|
+
}, " Slug ", -1)), withDirectives(createElementVNode("input", {
|
|
163
|
+
id: "publish-document-slug",
|
|
164
|
+
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => slug.value = $event),
|
|
165
|
+
autocomplete: "off",
|
|
166
|
+
class: "border-border bg-b-1 text-c-1 placeholder:text-c-3 focus:border-c-accent h-8 rounded border px-3 text-sm outline-none",
|
|
167
|
+
placeholder: "pets-api",
|
|
168
|
+
type: "text"
|
|
169
|
+
}, null, 512), [[vModelText, slug.value]])]),
|
|
170
|
+
createElementVNode("div", _hoisted_8, [_cache[7] || (_cache[7] = createElementVNode("label", {
|
|
171
|
+
class: "text-c-1 text-xs font-medium",
|
|
172
|
+
for: "publish-document-version"
|
|
173
|
+
}, " Version ", -1)), withDirectives(createElementVNode("input", {
|
|
174
|
+
id: "publish-document-version",
|
|
175
|
+
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => version.value = $event),
|
|
176
|
+
autocomplete: "off",
|
|
177
|
+
class: "border-border bg-b-1 text-c-1 placeholder:text-c-3 focus:border-c-accent h-8 rounded border px-3 text-sm outline-none",
|
|
178
|
+
placeholder: "1.0.0",
|
|
179
|
+
type: "text"
|
|
180
|
+
}, null, 512), [[vModelText, version.value]])]),
|
|
181
|
+
publishPreview.value ? (openBlock(), createElementBlock("div", _hoisted_9, [_cache[8] || (_cache[8] = createTextVNode(" Publishing as ", -1)), createElementVNode("span", _hoisted_10, toDisplayString(publishPreview.value), 1)])) : createCommentVNode("", true),
|
|
182
|
+
errorMessage.value ? (openBlock(), createElementBlock("p", _hoisted_11, toDisplayString(errorMessage.value), 1)) : createCommentVNode("", true),
|
|
183
|
+
createElementVNode("div", _hoisted_12, [createVNode(unref(ScalarButton), {
|
|
184
|
+
size: "sm",
|
|
185
|
+
type: "button",
|
|
186
|
+
variant: "ghost",
|
|
187
|
+
onClick: _cache[3] || (_cache[3] = ($event) => __props.state.hide())
|
|
188
|
+
}, {
|
|
189
|
+
default: withCtx(() => [..._cache[9] || (_cache[9] = [createTextVNode(" Cancel ", -1)])]),
|
|
190
|
+
_: 1
|
|
191
|
+
}), createVNode(unref(ScalarButton), {
|
|
192
|
+
disabled: isSubmitDisabled.value,
|
|
193
|
+
loader: unref(loader),
|
|
194
|
+
size: "sm",
|
|
195
|
+
type: "submit"
|
|
196
|
+
}, {
|
|
197
|
+
default: withCtx(() => [..._cache[10] || (_cache[10] = [createTextVNode(" Publish ", -1)])]),
|
|
198
|
+
_: 1
|
|
199
|
+
}, 8, ["disabled", "loader"])])
|
|
200
|
+
], 32)]),
|
|
201
|
+
_: 1
|
|
202
|
+
}, 8, ["state"]);
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
//#endregion
|
|
207
|
+
export { PublishDocumentModal_vue_vue_type_script_setup_true_lang_default as default };
|
|
208
|
+
|
|
209
|
+
//# sourceMappingURL=PublishDocumentModal.vue.script.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PublishDocumentModal.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/PublishDocumentModal.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Modal that walks the user through publishing a brand-new document to\n * the registry.\n *\n * The component is purely presentational: it owns the form state, the\n * loading / error indicator and the validation rules, and delegates the\n * actual publish call to the parent through `submit`. The parent\n * resolves `submit` with `{ ok: true }` to close the modal or\n * `{ ok: false, message }` to surface the error inline so the user can\n * fix the input and retry without losing what they typed.\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarListbox,\n ScalarModal,\n useLoadingState,\n type ModalState,\n type ScalarListboxOption,\n} from '@scalar/components'\nimport { slugify } from '@scalar/helpers/string/slugify'\nimport { ScalarIconBuildings, ScalarIconCaretDown } from '@scalar/icons'\nimport { computed, ref, watch } from 'vue'\n\nimport type { RegistryNamespacesState } from '@/v2/types/configuration'\n\nconst {\n state,\n namespaces,\n defaultSlug = '',\n defaultVersion = '1.0.0',\n} = defineProps<{\n /** Modal control returned by `useModal()`. */\n state: ModalState\n /** Namespaces the user can publish into, with loading status. */\n namespaces: RegistryNamespacesState\n /**\n * Initial slug to seed the slug input with. The parent typically\n * computes this from the active document's title. Falls back to an\n * empty string so the placeholder is visible.\n */\n defaultSlug?: string\n /**\n * Initial version. Defaults to `1.0.0` when the active document does\n * not advertise an `info.version` yet.\n */\n defaultVersion?: string\n}>()\n\nconst emit = defineEmits<{\n /**\n * Fired when the user submits a valid form. The parent must resolve\n * the returned promise with either `{ ok: true }` (modal closes) or\n * `{ ok: false, message }` (message is rendered inline so the user\n * can retry).\n */\n (\n event: 'submit',\n payload: {\n input: { namespace: string; slug: string; version: string }\n done: (outcome: { ok: true } | { ok: false; message: string }) => void\n },\n ): void\n}>()\n\nconst namespaceOptions = computed<ScalarListboxOption[]>(() =>\n (namespaces.status === 'success'\n ? namespaces.namespaces\n : (namespaces.namespaces ?? [])\n ).map((entry) => ({\n id: entry.namespace,\n label: entry.title?.trim() || entry.namespace,\n })),\n)\n\nconst isNamespacesLoading = computed(() => namespaces.status === 'loading')\n\n/**\n * The picker component is only rendered when there is more than one\n * namespace to choose between. Single-namespace setups read as a static\n * label so the user does not have to interact with a one-item dropdown.\n */\nconst hasMultipleNamespaces = computed(() => namespaceOptions.value.length > 1)\n\nconst selectedNamespace = ref<ScalarListboxOption | undefined>(undefined)\nconst slug = ref('')\nconst version = ref('')\nconst errorMessage = ref<string | null>(null)\nconst loader = useLoadingState()\n\n/**\n * Resets the form whenever the modal opens. Keeping this scoped to the\n * `open` flag (and not to every prop change) preserves user input\n * across reactive updates while the modal is already on screen.\n */\nwatch(\n () => state.open,\n (isOpen) => {\n if (!isOpen) {\n return\n }\n slug.value = slugify(defaultSlug)\n version.value = defaultVersion\n errorMessage.value = null\n selectedNamespace.value = namespaceOptions.value[0]\n void loader.clear()\n },\n)\n\n/** Keep the listbox selection in sync when the namespace list arrives. */\nwatch(namespaceOptions, (options) => {\n if (selectedNamespace.value) {\n return\n }\n selectedNamespace.value = options[0]\n})\n\nconst trimmedSlug = computed(() => slug.value.trim())\nconst trimmedVersion = computed(() => version.value.trim())\n\n/**\n * Human-friendly preview of the resulting registry coordinate\n * (`namespace/slug@version`). Returns `null` while any of the three\n * pieces is still missing so we never show a half-rendered path.\n * Mirrors the same fields submitted to the publish call so the user\n * sees exactly what will land on the registry.\n */\nconst publishPreview = computed<string | null>(() => {\n const namespace = selectedNamespace.value?.id\n if (!namespace || !trimmedSlug.value || !trimmedVersion.value) {\n return null\n }\n return `${namespace}/${trimmedSlug.value}@${trimmedVersion.value}`\n})\n\nconst isSubmitDisabled = computed(\n () =>\n !selectedNamespace.value ||\n trimmedSlug.value.length === 0 ||\n trimmedVersion.value.length === 0 ||\n loader.isLoading,\n)\n\nconst handleSubmit = (): void => {\n errorMessage.value = null\n\n const namespace = selectedNamespace.value?.id\n if (!namespace || !trimmedSlug.value || !trimmedVersion.value) {\n return\n }\n\n loader.start()\n\n emit('submit', {\n input: {\n namespace: String(namespace),\n slug: trimmedSlug.value,\n version: trimmedVersion.value,\n },\n done: (outcome) => {\n if (outcome.ok) {\n void loader.clear()\n state.hide()\n return\n }\n errorMessage.value = outcome.message\n void loader.invalidate()\n },\n })\n}\n</script>\n\n<template>\n <ScalarModal\n size=\"sm\"\n :state=\"state\"\n title=\"Publish to registry\"\n variant=\"form\">\n <form\n class=\"flex flex-col gap-4\"\n @submit.prevent=\"handleSubmit\">\n <!--\n Namespace picker.\n\n All three states (single namespace, multiple namespaces, loading)\n render into a bordered field of the same height as the inputs\n below so the modal reads as one cohesive form. A leading\n `Buildings` icon anchors the namespace context visually so the\n single-value state does not look like loose plain text.\n\n - One namespace -> read-only field with the namespace label and\n a \"fixed\" hint, so the user understands there is nothing to\n pick without staring at a one-item dropdown.\n - Multiple namespaces -> dropdown listbox.\n - Loading -> the same field shape with a skeleton-style label\n so layout does not shift once the list arrives.\n -->\n <div class=\"flex flex-col gap-1.5\">\n <label class=\"text-c-1 text-xs font-medium\">Namespace</label>\n <template v-if=\"isNamespacesLoading && namespaceOptions.length === 0\">\n <div\n class=\"border-border bg-b-2 flex h-8 items-center gap-2 rounded border px-3 text-sm\">\n <ScalarIconBuildings\n class=\"text-c-3 size-3.5 shrink-0\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"text-c-3\">Loading namespaces…</span>\n </div>\n </template>\n <template v-else-if=\"hasMultipleNamespaces\">\n <ScalarListbox\n v-model=\"selectedNamespace\"\n :options=\"namespaceOptions\"\n teleport>\n <ScalarButton\n class=\"border-border text-c-1 hover:bg-b-2 flex h-8 w-full items-center justify-between gap-2 rounded border px-3 font-normal\"\n fullWidth\n type=\"button\"\n variant=\"outlined\">\n <span class=\"flex min-w-0 items-center gap-2\">\n <ScalarIconBuildings\n class=\"text-c-2 size-3.5 shrink-0\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"truncate\">{{\n selectedNamespace?.label ?? 'Select a namespace'\n }}</span>\n </span>\n <ScalarIconCaretDown\n class=\"text-c-2 size-3.5 shrink-0\"\n size=\"sm\"\n thickness=\"1.5\" />\n </ScalarButton>\n </ScalarListbox>\n </template>\n <template v-else>\n <div\n class=\"border-border bg-b-2 text-c-1 flex h-8 items-center gap-2 rounded border px-3 text-sm\"\n :title=\"\n selectedNamespace\n ? `Publishing to ${selectedNamespace.label}`\n : undefined\n \">\n <ScalarIconBuildings\n class=\"text-c-2 size-3.5 shrink-0\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span class=\"truncate\">\n {{ selectedNamespace?.label ?? '—' }}\n </span>\n </div>\n </template>\n </div>\n\n <!-- Slug input. Pre-filled with a slugified version of the document title. -->\n <div class=\"flex flex-col gap-1.5\">\n <label\n class=\"text-c-1 text-xs font-medium\"\n for=\"publish-document-slug\">\n Slug\n </label>\n <input\n id=\"publish-document-slug\"\n v-model=\"slug\"\n autocomplete=\"off\"\n class=\"border-border bg-b-1 text-c-1 placeholder:text-c-3 focus:border-c-accent h-8 rounded border px-3 text-sm outline-none\"\n placeholder=\"pets-api\"\n type=\"text\" />\n </div>\n\n <!--\n Version input. The string lives on `info.version` after a\n successful publish so the local document and the registry stay\n in sync without an extra edit.\n -->\n <div class=\"flex flex-col gap-1.5\">\n <label\n class=\"text-c-1 text-xs font-medium\"\n for=\"publish-document-version\">\n Version\n </label>\n <input\n id=\"publish-document-version\"\n v-model=\"version\"\n autocomplete=\"off\"\n class=\"border-border bg-b-1 text-c-1 placeholder:text-c-3 focus:border-c-accent h-8 rounded border px-3 text-sm outline-none\"\n placeholder=\"1.0.0\"\n type=\"text\" />\n </div>\n\n <!--\n Live preview of the published identity. Reading the modal\n top-down the user types a slug + version and sees the resulting\n coordinate update in place, which is more reassuring than\n decoding three separate fields. Hidden until enough fields\n resolve so we never render half a path.\n -->\n <div\n v-if=\"publishPreview\"\n class=\"text-c-2 -mt-1 text-xs\">\n Publishing as\n <span class=\"text-c-1 font-mono\">{{ publishPreview }}</span>\n </div>\n\n <!--\n Inline error region. Stays mounted when present so the user can\n read the message while they are correcting the form rather than\n having it disappear after a toast timeout.\n -->\n <p\n v-if=\"errorMessage\"\n class=\"text-red bg-b-2 rounded p-2 text-xs\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <div class=\"flex justify-end gap-2 pt-1\">\n <ScalarButton\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"state.hide()\">\n Cancel\n </ScalarButton>\n <ScalarButton\n :disabled=\"isSubmitDisabled\"\n :loader=\"loader\"\n size=\"sm\"\n type=\"submit\">\n Publish\n </ScalarButton>\n </div>\n </form>\n </ScalarModal>\n</template>\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SyncConflictResolutionEditor.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/components/SyncConflictResolutionEditor.vue"],"names":[],"mappings":"AA0UA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAOpD,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,CAAA;IAChD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC1C,CAAC;AA+UF,QAAA,MAAM,YAAY;;0BAxUM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;0BAAvB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;kFA2U7C,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import _plugin_vue_export_helper_default from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
2
2
|
import SyncConflictResolutionEditor_vue_vue_type_script_setup_true_lang_default from "./SyncConflictResolutionEditor.vue.script.js";
|
|
3
3
|
/* empty css */
|
|
4
|
-
//#region src/v2/features/
|
|
5
|
-
var SyncConflictResolutionEditor_default = /* @__PURE__ */ _plugin_vue_export_helper_default(SyncConflictResolutionEditor_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-
|
|
4
|
+
//#region src/v2/features/app/components/SyncConflictResolutionEditor.vue
|
|
5
|
+
var SyncConflictResolutionEditor_default = /* @__PURE__ */ _plugin_vue_export_helper_default(SyncConflictResolutionEditor_vue_vue_type_script_setup_true_lang_default, [["__scopeId", "data-v-65ed5337"]]);
|
|
6
6
|
//#endregion
|
|
7
7
|
export { SyncConflictResolutionEditor_default as default };
|
|
8
8
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SyncConflictResolutionEditor.vue.js","names":[],"sources":["../../../../../src/v2/features/app/components/SyncConflictResolutionEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { type merge } from '@scalar/json-magic/diff'\nimport { useToasts } from '@scalar/use-toasts'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport { useSplitResize } from '@/v2/components/resize'\nimport { useThreeWayMergeEditor } from '@/v2/features/editor/hooks/use-three-way-merge-editor'\n\nconst { conflicts, baseDocument, resolvedDocument } = defineProps<{\n conflicts: ReturnType<typeof merge>['conflicts']\n baseDocument: Record<string, unknown>\n resolvedDocument: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n applyChanges: [\n payload: {\n resolvedDocument: Record<string, unknown>\n },\n ]\n}>()\n\nconst { toast } = useToasts()\n\nconst splitContainerRef = ref<HTMLDivElement>()\nconst topEditorsRowRef = ref<HTMLDivElement>()\nconst topPaneSize = ref(50)\nconst leftPaneSize = ref(50)\n\nconst { onHorizontalResizeStart, onVerticalResizeStart, stopActiveResize } =\n useSplitResize({\n horizontalContainerRef: topEditorsRowRef,\n verticalContainerRef: splitContainerRef,\n leftPaneSize,\n topPaneSize,\n horizontalMin: 20,\n horizontalMax: 80,\n verticalMin: 25,\n verticalMax: 75,\n })\n\nconst mergeEditor = useThreeWayMergeEditor({\n baseDocument,\n resolvedDocument,\n conflicts,\n onApplyChanges: (resolvedDoc) =>\n emit('applyChanges', { resolvedDocument: resolvedDoc }),\n onError: (message) => toast(message, 'error'),\n})\n\nconst conflictsLeft = computed(() => mergeEditor.conflictsLeft.value)\n\nconst topPaneStyle = computed(() => ({ height: `${topPaneSize.value}%` }))\nconst leftPaneStyle = computed(() => ({ width: `${leftPaneSize.value}%` }))\nconst rightPaneStyle = computed(() => ({\n width: `${100 - leftPaneSize.value}%`,\n}))\n\nconst localChangesEditorRef = ref<HTMLDivElement>()\nconst remoteChangesEditorRef = ref<HTMLDivElement>()\nconst resultEditorRef = ref<HTMLDivElement>()\n\nonMounted(() => {\n const localEl = localChangesEditorRef.value\n const remoteEl = remoteChangesEditorRef.value\n const resultEl = resultEditorRef.value\n if (localEl && remoteEl && resultEl) {\n mergeEditor.init({\n local: localEl,\n remote: remoteEl,\n result: resultEl,\n })\n }\n})\n\nonUnmounted(() => {\n stopActiveResize()\n mergeEditor.dispose()\n})\n</script>\n\n<template>\n <p class=\"text-c-2 text-xs\">\n Resolve conflicts inline in the full document editor. Use the buttons inside\n the editor for each conflict.\n </p>\n\n <div\n ref=\"splitContainerRef\"\n class=\"flex min-h-0 flex-1 flex-col overflow-hidden\">\n <div\n ref=\"topEditorsRowRef\"\n class=\"flex min-h-0 gap-1 p-1\"\n :style=\"topPaneStyle\">\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"leftPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Current\n </div>\n <div\n ref=\"localChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <button\n aria-label=\"Resize current and remote editors\"\n class=\"resize-handle resize-handle-col\"\n type=\"button\"\n @pointerdown=\"onHorizontalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"rightPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Remote\n </div>\n <div\n ref=\"remoteChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n </div>\n\n <button\n aria-label=\"Resize top and result editors\"\n class=\"resize-handle resize-handle-row\"\n type=\"button\"\n @pointerdown=\"onVerticalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 mx-1 mb-1 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 flex shrink-0 items-center justify-between border-b px-2 py-1 text-[11px]\">\n <span> Result </span>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-c-2 text-[11px] normal-case\">\n {{ conflictsLeft }} conflict{{ conflictsLeft === 1 ? '' : 's' }}\n left\n </span>\n <ScalarButton\n :disabled=\"conflictsLeft === 0\"\n size=\"xs\"\n type=\"button\"\n variant=\"outlined\"\n @click=\"mergeEditor.goToNextConflict\">\n Next Conflict\n </ScalarButton>\n </div>\n </div>\n <div\n ref=\"resultEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <div class=\"flex shrink-0 items-center justify-end gap-2\">\n <ScalarButton\n :disabled=\"conflictsLeft > 0\"\n size=\"xs\"\n type=\"button\"\n @click=\"mergeEditor.applyResolvedConflicts\">\n Apply changes\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n.sync-layout-root {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 96%,\n transparent\n );\n}\n\n.sync-editor-pane {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 95%,\n transparent\n );\n}\n\n.sync-pane-title {\n letter-spacing: 0.03em;\n text-transform: uppercase;\n font-weight: 600;\n background: color-mix(\n in srgb,\n var(--scalar-color-background-2, #2d2d30) 85%,\n transparent\n );\n}\n\n.resize-handle {\n position: relative;\n display: block;\n flex-shrink: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n transition:\n background-color 0.12s ease,\n box-shadow 0.12s ease;\n}\n\n.resize-handle::before {\n content: '';\n position: absolute;\n border-radius: 999px;\n opacity: 1;\n transition:\n background-color 0.12s ease,\n transform 0.12s ease;\n}\n\n.resize-handle:hover {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 12%,\n transparent\n );\n}\n\n.resize-handle:active {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 18%,\n transparent\n );\n}\n\n.resize-handle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px\n color-mix(in srgb, var(--scalar-color-accent, #007acc) 70%, transparent);\n}\n\n.resize-handle-col {\n width: 8px;\n min-height: 44px;\n margin: 2px 0;\n cursor: col-resize;\n}\n\n.resize-handle-col::before {\n top: 50%;\n left: 50%;\n width: 1px;\n height: calc(100% - 8px);\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-row {\n height: 8px;\n margin: 0 4px;\n cursor: row-resize;\n}\n\n.resize-handle-row::before {\n top: 50%;\n left: 50%;\n width: calc(100% - 8px);\n height: 1px;\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-col:hover::before,\n.resize-handle-col:active::before,\n.resize-handle-row:hover::before,\n.resize-handle-row:active::before {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 78%,\n transparent\n );\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight-box-single) {\n border: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-top) {\n border-top: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-middle) {\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-bottom) {\n border-bottom: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #fde047 35%, transparent);\n box-sizing: border-box;\n}\n</style>\n"],"mappings":""}
|
package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.script.js
RENAMED
|
@@ -3,7 +3,7 @@ import { useThreeWayMergeEditor } from "../../editor/hooks/use-three-way-merge-e
|
|
|
3
3
|
import { Fragment, computed, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, normalizeStyle, onMounted, onUnmounted, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
|
|
4
4
|
import { ScalarButton } from "@scalar/components";
|
|
5
5
|
import { useToasts } from "@scalar/use-toasts";
|
|
6
|
-
//#region src/v2/features/
|
|
6
|
+
//#region src/v2/features/app/components/SyncConflictResolutionEditor.vue?vue&type=script&setup=true&lang.ts
|
|
7
7
|
var _hoisted_1 = { class: "sync-editor-pane border-c-3 mx-1 mb-1 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border" };
|
|
8
8
|
var _hoisted_2 = { class: "sync-pane-title text-c-2 border-c-3 flex shrink-0 items-center justify-between border-b px-2 py-1 text-[11px]" };
|
|
9
9
|
var _hoisted_3 = { class: "flex items-center gap-2" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SyncConflictResolutionEditor.vue.js","names":[],"sources":["../../../../../src/v2/features/collection/components/SyncConflictResolutionEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { type merge } from '@scalar/json-magic/diff'\nimport { useToasts } from '@scalar/use-toasts'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport { useSplitResize } from '@/v2/components/resize'\nimport { useThreeWayMergeEditor } from '@/v2/features/editor/hooks/use-three-way-merge-editor'\n\nconst { conflicts, baseDocument, resolvedDocument } = defineProps<{\n conflicts: ReturnType<typeof merge>['conflicts']\n baseDocument: Record<string, unknown>\n resolvedDocument: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n applyChanges: [\n payload: {\n resolvedDocument: Record<string, unknown>\n },\n ]\n}>()\n\nconst { toast } = useToasts()\n\nconst splitContainerRef = ref<HTMLDivElement>()\nconst topEditorsRowRef = ref<HTMLDivElement>()\nconst topPaneSize = ref(50)\nconst leftPaneSize = ref(50)\n\nconst { onHorizontalResizeStart, onVerticalResizeStart, stopActiveResize } =\n useSplitResize({\n horizontalContainerRef: topEditorsRowRef,\n verticalContainerRef: splitContainerRef,\n leftPaneSize,\n topPaneSize,\n horizontalMin: 20,\n horizontalMax: 80,\n verticalMin: 25,\n verticalMax: 75,\n })\n\nconst mergeEditor = useThreeWayMergeEditor({\n baseDocument,\n resolvedDocument,\n conflicts,\n onApplyChanges: (resolvedDoc) =>\n emit('applyChanges', { resolvedDocument: resolvedDoc }),\n onError: (message) => toast(message, 'error'),\n})\n\nconst conflictsLeft = computed(() => mergeEditor.conflictsLeft.value)\n\nconst topPaneStyle = computed(() => ({ height: `${topPaneSize.value}%` }))\nconst leftPaneStyle = computed(() => ({ width: `${leftPaneSize.value}%` }))\nconst rightPaneStyle = computed(() => ({\n width: `${100 - leftPaneSize.value}%`,\n}))\n\nconst localChangesEditorRef = ref<HTMLDivElement>()\nconst remoteChangesEditorRef = ref<HTMLDivElement>()\nconst resultEditorRef = ref<HTMLDivElement>()\n\nonMounted(() => {\n const localEl = localChangesEditorRef.value\n const remoteEl = remoteChangesEditorRef.value\n const resultEl = resultEditorRef.value\n if (localEl && remoteEl && resultEl) {\n mergeEditor.init({\n local: localEl,\n remote: remoteEl,\n result: resultEl,\n })\n }\n})\n\nonUnmounted(() => {\n stopActiveResize()\n mergeEditor.dispose()\n})\n</script>\n\n<template>\n <p class=\"text-c-2 text-xs\">\n Resolve conflicts inline in the full document editor. Use the buttons inside\n the editor for each conflict.\n </p>\n\n <div\n ref=\"splitContainerRef\"\n class=\"flex min-h-0 flex-1 flex-col overflow-hidden\">\n <div\n ref=\"topEditorsRowRef\"\n class=\"flex min-h-0 gap-1 p-1\"\n :style=\"topPaneStyle\">\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"leftPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Current\n </div>\n <div\n ref=\"localChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <button\n aria-label=\"Resize current and remote editors\"\n class=\"resize-handle resize-handle-col\"\n type=\"button\"\n @pointerdown=\"onHorizontalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"rightPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Remote\n </div>\n <div\n ref=\"remoteChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n </div>\n\n <button\n aria-label=\"Resize top and result editors\"\n class=\"resize-handle resize-handle-row\"\n type=\"button\"\n @pointerdown=\"onVerticalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 mx-1 mb-1 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 flex shrink-0 items-center justify-between border-b px-2 py-1 text-[11px]\">\n <span> Result </span>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-c-2 text-[11px] normal-case\">\n {{ conflictsLeft }} conflict{{ conflictsLeft === 1 ? '' : 's' }}\n left\n </span>\n <ScalarButton\n :disabled=\"conflictsLeft === 0\"\n size=\"xs\"\n type=\"button\"\n variant=\"outlined\"\n @click=\"mergeEditor.goToNextConflict\">\n Next Conflict\n </ScalarButton>\n </div>\n </div>\n <div\n ref=\"resultEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <div class=\"flex shrink-0 items-center justify-end gap-2\">\n <ScalarButton\n :disabled=\"conflictsLeft > 0\"\n size=\"xs\"\n type=\"button\"\n @click=\"mergeEditor.applyResolvedConflicts\">\n Apply changes\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n.sync-layout-root {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 96%,\n transparent\n );\n}\n\n.sync-editor-pane {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 95%,\n transparent\n );\n}\n\n.sync-pane-title {\n letter-spacing: 0.03em;\n text-transform: uppercase;\n font-weight: 600;\n background: color-mix(\n in srgb,\n var(--scalar-color-background-2, #2d2d30) 85%,\n transparent\n );\n}\n\n.resize-handle {\n position: relative;\n display: block;\n flex-shrink: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n transition:\n background-color 0.12s ease,\n box-shadow 0.12s ease;\n}\n\n.resize-handle::before {\n content: '';\n position: absolute;\n border-radius: 999px;\n opacity: 1;\n transition:\n background-color 0.12s ease,\n transform 0.12s ease;\n}\n\n.resize-handle:hover {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 12%,\n transparent\n );\n}\n\n.resize-handle:active {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 18%,\n transparent\n );\n}\n\n.resize-handle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px\n color-mix(in srgb, var(--scalar-color-accent, #007acc) 70%, transparent);\n}\n\n.resize-handle-col {\n width: 8px;\n min-height: 44px;\n margin: 2px 0;\n cursor: col-resize;\n}\n\n.resize-handle-col::before {\n top: 50%;\n left: 50%;\n width: 1px;\n height: calc(100% - 8px);\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-row {\n height: 8px;\n margin: 0 4px;\n cursor: row-resize;\n}\n\n.resize-handle-row::before {\n top: 50%;\n left: 50%;\n width: calc(100% - 8px);\n height: 1px;\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-col:hover::before,\n.resize-handle-col:active::before,\n.resize-handle-row:hover::before,\n.resize-handle-row:active::before {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 78%,\n transparent\n );\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight-box-single) {\n border: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-top) {\n border-top: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-middle) {\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-bottom) {\n border-bottom: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #fde047 35%, transparent);\n box-sizing: border-box;\n}\n</style>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"SyncConflictResolutionEditor.vue.script.js","names":[],"sources":["../../../../../src/v2/features/app/components/SyncConflictResolutionEditor.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { type merge } from '@scalar/json-magic/diff'\nimport { useToasts } from '@scalar/use-toasts'\nimport { computed, onMounted, onUnmounted, ref } from 'vue'\n\nimport { useSplitResize } from '@/v2/components/resize'\nimport { useThreeWayMergeEditor } from '@/v2/features/editor/hooks/use-three-way-merge-editor'\n\nconst { conflicts, baseDocument, resolvedDocument } = defineProps<{\n conflicts: ReturnType<typeof merge>['conflicts']\n baseDocument: Record<string, unknown>\n resolvedDocument: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{\n applyChanges: [\n payload: {\n resolvedDocument: Record<string, unknown>\n },\n ]\n}>()\n\nconst { toast } = useToasts()\n\nconst splitContainerRef = ref<HTMLDivElement>()\nconst topEditorsRowRef = ref<HTMLDivElement>()\nconst topPaneSize = ref(50)\nconst leftPaneSize = ref(50)\n\nconst { onHorizontalResizeStart, onVerticalResizeStart, stopActiveResize } =\n useSplitResize({\n horizontalContainerRef: topEditorsRowRef,\n verticalContainerRef: splitContainerRef,\n leftPaneSize,\n topPaneSize,\n horizontalMin: 20,\n horizontalMax: 80,\n verticalMin: 25,\n verticalMax: 75,\n })\n\nconst mergeEditor = useThreeWayMergeEditor({\n baseDocument,\n resolvedDocument,\n conflicts,\n onApplyChanges: (resolvedDoc) =>\n emit('applyChanges', { resolvedDocument: resolvedDoc }),\n onError: (message) => toast(message, 'error'),\n})\n\nconst conflictsLeft = computed(() => mergeEditor.conflictsLeft.value)\n\nconst topPaneStyle = computed(() => ({ height: `${topPaneSize.value}%` }))\nconst leftPaneStyle = computed(() => ({ width: `${leftPaneSize.value}%` }))\nconst rightPaneStyle = computed(() => ({\n width: `${100 - leftPaneSize.value}%`,\n}))\n\nconst localChangesEditorRef = ref<HTMLDivElement>()\nconst remoteChangesEditorRef = ref<HTMLDivElement>()\nconst resultEditorRef = ref<HTMLDivElement>()\n\nonMounted(() => {\n const localEl = localChangesEditorRef.value\n const remoteEl = remoteChangesEditorRef.value\n const resultEl = resultEditorRef.value\n if (localEl && remoteEl && resultEl) {\n mergeEditor.init({\n local: localEl,\n remote: remoteEl,\n result: resultEl,\n })\n }\n})\n\nonUnmounted(() => {\n stopActiveResize()\n mergeEditor.dispose()\n})\n</script>\n\n<template>\n <p class=\"text-c-2 text-xs\">\n Resolve conflicts inline in the full document editor. Use the buttons inside\n the editor for each conflict.\n </p>\n\n <div\n ref=\"splitContainerRef\"\n class=\"flex min-h-0 flex-1 flex-col overflow-hidden\">\n <div\n ref=\"topEditorsRowRef\"\n class=\"flex min-h-0 gap-1 p-1\"\n :style=\"topPaneStyle\">\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"leftPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Current\n </div>\n <div\n ref=\"localChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <button\n aria-label=\"Resize current and remote editors\"\n class=\"resize-handle resize-handle-col\"\n type=\"button\"\n @pointerdown=\"onHorizontalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 flex min-h-0 flex-col overflow-hidden rounded-lg border\"\n :style=\"rightPaneStyle\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 shrink-0 border-b px-2 py-1 text-[11px]\">\n Remote\n </div>\n <div\n ref=\"remoteChangesEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n </div>\n\n <button\n aria-label=\"Resize top and result editors\"\n class=\"resize-handle resize-handle-row\"\n type=\"button\"\n @pointerdown=\"onVerticalResizeStart\" />\n\n <div\n class=\"sync-editor-pane border-c-3 mx-1 mb-1 flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border\">\n <div\n class=\"sync-pane-title text-c-2 border-c-3 flex shrink-0 items-center justify-between border-b px-2 py-1 text-[11px]\">\n <span> Result </span>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-c-2 text-[11px] normal-case\">\n {{ conflictsLeft }} conflict{{ conflictsLeft === 1 ? '' : 's' }}\n left\n </span>\n <ScalarButton\n :disabled=\"conflictsLeft === 0\"\n size=\"xs\"\n type=\"button\"\n variant=\"outlined\"\n @click=\"mergeEditor.goToNextConflict\">\n Next Conflict\n </ScalarButton>\n </div>\n </div>\n <div\n ref=\"resultEditorRef\"\n class=\"min-h-0 flex-1\"></div>\n </div>\n\n <div class=\"flex shrink-0 items-center justify-end gap-2\">\n <ScalarButton\n :disabled=\"conflictsLeft > 0\"\n size=\"xs\"\n type=\"button\"\n @click=\"mergeEditor.applyResolvedConflicts\">\n Apply changes\n </ScalarButton>\n </div>\n </div>\n</template>\n<style scoped>\n.editor-container {\n width: 100%;\n height: 100%;\n}\n\n.sync-layout-root {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 96%,\n transparent\n );\n}\n\n.sync-editor-pane {\n background: color-mix(\n in srgb,\n var(--scalar-color-background-1, #1e1e1e) 95%,\n transparent\n );\n}\n\n.sync-pane-title {\n letter-spacing: 0.03em;\n text-transform: uppercase;\n font-weight: 600;\n background: color-mix(\n in srgb,\n var(--scalar-color-background-2, #2d2d30) 85%,\n transparent\n );\n}\n\n.resize-handle {\n position: relative;\n display: block;\n flex-shrink: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n transition:\n background-color 0.12s ease,\n box-shadow 0.12s ease;\n}\n\n.resize-handle::before {\n content: '';\n position: absolute;\n border-radius: 999px;\n opacity: 1;\n transition:\n background-color 0.12s ease,\n transform 0.12s ease;\n}\n\n.resize-handle:hover {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 12%,\n transparent\n );\n}\n\n.resize-handle:active {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 18%,\n transparent\n );\n}\n\n.resize-handle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 1px\n color-mix(in srgb, var(--scalar-color-accent, #007acc) 70%, transparent);\n}\n\n.resize-handle-col {\n width: 8px;\n min-height: 44px;\n margin: 2px 0;\n cursor: col-resize;\n}\n\n.resize-handle-col::before {\n top: 50%;\n left: 50%;\n width: 1px;\n height: calc(100% - 8px);\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-row {\n height: 8px;\n margin: 0 4px;\n cursor: row-resize;\n}\n\n.resize-handle-row::before {\n top: 50%;\n left: 50%;\n width: calc(100% - 8px);\n height: 1px;\n transform: translate(-50%, -50%);\n background: color-mix(\n in srgb,\n var(--scalar-color-border, #3c3c3c) 85%,\n transparent\n );\n}\n\n.resize-handle-col:hover::before,\n.resize-handle-col:active::before,\n.resize-handle-row:hover::before,\n.resize-handle-row:active::before {\n background: color-mix(\n in srgb,\n var(--scalar-color-accent, #007acc) 78%,\n transparent\n );\n}\n\n:deep(.json-path-highlight) {\n background-color: rgba(255, 200, 0, 0.35);\n border-radius: 4px;\n}\n\n:deep(.json-focus-highlight-box-single) {\n border: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-top) {\n border-top: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-middle) {\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-sizing: border-box;\n}\n\n:deep(.json-focus-highlight-box-bottom) {\n border-bottom: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-left: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n border-right: 2px solid color-mix(in srgb, #facc15 90%, #eab308 10%);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, #fde047 35%, transparent);\n box-sizing: border-box;\n}\n</style>\n"],"mappings":"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"}
|
|
@@ -21,12 +21,15 @@ type CheckVersionConflictResult = {
|
|
|
21
21
|
* - the original document (last-known remote, kept in
|
|
22
22
|
* `workspaceStore.getOriginalDocument`),
|
|
23
23
|
* - the editable workspace document (with the user's local edits),
|
|
24
|
-
* - the freshly-fetched remote document
|
|
24
|
+
* - the freshly-fetched remote document.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
26
|
+
* `registryCommitHash` is the hash advertised by the registry version
|
|
27
|
+
* listing and acts as the pre-fetch cache key: when it matches the
|
|
28
|
+
* previously stored `conflictCheckedAgainstHash`, we return the cached
|
|
29
|
+
* result without touching the network. After a fresh fetch, the cache
|
|
30
|
+
* is rewritten with the authoritative `versionSha` returned alongside
|
|
31
|
+
* the document body so subsequent comparisons settle on the hash the
|
|
32
|
+
* registry actually served.
|
|
30
33
|
*
|
|
31
34
|
* Writing to `x-scalar-registry-meta` does not flip the document's
|
|
32
35
|
* `x-scalar-is-dirty` flag (the workspace store excludes registry meta from
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-version-conflict.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAEpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAI1E,iDAAiD;AACjD,KAAK,0BAA0B,GAC3B;IACE,EAAE,EAAE,IAAI,CAAA;IACR,WAAW,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhC
|
|
1
|
+
{"version":3,"file":"check-version-conflict.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAEpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAI1E,iDAAiD;AACjD,KAAK,0BAA0B,GAC3B;IACE,EAAE,EAAE,IAAI,CAAA;IACR,WAAW,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAA;CACnB,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,oBAAoB,GAAU,0FAQxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B,KAAG,OAAO,CAAC,0BAA0B,CAiDrC,CAAA"}
|
|
@@ -8,12 +8,15 @@ import { detectDocumentConflicts } from "./detect-document-conflicts.js";
|
|
|
8
8
|
* - the original document (last-known remote, kept in
|
|
9
9
|
* `workspaceStore.getOriginalDocument`),
|
|
10
10
|
* - the editable workspace document (with the user's local edits),
|
|
11
|
-
* - the freshly-fetched remote document
|
|
11
|
+
* - the freshly-fetched remote document.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* `registryCommitHash` is the hash advertised by the registry version
|
|
14
|
+
* listing and acts as the pre-fetch cache key: when it matches the
|
|
15
|
+
* previously stored `conflictCheckedAgainstHash`, we return the cached
|
|
16
|
+
* result without touching the network. After a fresh fetch, the cache
|
|
17
|
+
* is rewritten with the authoritative `versionSha` returned alongside
|
|
18
|
+
* the document body so subsequent comparisons settle on the hash the
|
|
19
|
+
* registry actually served.
|
|
17
20
|
*
|
|
18
21
|
* Writing to `x-scalar-registry-meta` does not flip the document's
|
|
19
22
|
* `x-scalar-is-dirty` flag (the workspace store excludes registry meta from
|
|
@@ -53,7 +56,7 @@ var checkVersionConflict = async ({ workspaceStore, fetcher, documentName, names
|
|
|
53
56
|
const hasConflict = detectDocumentConflicts({
|
|
54
57
|
original,
|
|
55
58
|
local: document,
|
|
56
|
-
remote: result.data
|
|
59
|
+
remote: result.data.document
|
|
57
60
|
});
|
|
58
61
|
document["x-scalar-registry-meta"] = {
|
|
59
62
|
...meta ?? {
|
|
@@ -64,7 +67,7 @@ var checkVersionConflict = async ({ workspaceStore, fetcher, documentName, names
|
|
|
64
67
|
namespace,
|
|
65
68
|
slug,
|
|
66
69
|
version,
|
|
67
|
-
conflictCheckedAgainstHash:
|
|
70
|
+
conflictCheckedAgainstHash: result.data.versionSha,
|
|
68
71
|
hasConflict
|
|
69
72
|
};
|
|
70
73
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-version-conflict.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"sourcesContent":["import type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nimport { detectDocumentConflicts } from './detect-document-conflicts'\n\n/** Result returned by `checkVersionConflict`. */\ntype CheckVersionConflictResult =\n | {\n ok: true\n hasConflict: boolean\n /**\n * `true` when the result was already cached on the document for the\n * current registry hash and we did not have to fetch / recompute.\n */\n fromCache: boolean\n }\n | { ok: false; error: string }\n\n/**\n * Compute (or reuse a cached) conflict-check result for a registry-backed\n * workspace document and persist it on `x-scalar-registry-meta`.\n *\n * The check is a three-way merge between:\n * - the original document (last-known remote, kept in\n * `workspaceStore.getOriginalDocument`),\n * - the editable workspace document (with the user's local edits),\n * - the freshly-fetched remote document
|
|
1
|
+
{"version":3,"file":"check-version-conflict.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/check-version-conflict.ts"],"sourcesContent":["import type { WorkspaceStore } from '@scalar/workspace-store/client'\n\nimport type { ImportDocumentFromRegistry } from '@/v2/types/configuration'\n\nimport { detectDocumentConflicts } from './detect-document-conflicts'\n\n/** Result returned by `checkVersionConflict`. */\ntype CheckVersionConflictResult =\n | {\n ok: true\n hasConflict: boolean\n /**\n * `true` when the result was already cached on the document for the\n * current registry hash and we did not have to fetch / recompute.\n */\n fromCache: boolean\n }\n | { ok: false; error: string }\n\n/**\n * Compute (or reuse a cached) conflict-check result for a registry-backed\n * workspace document and persist it on `x-scalar-registry-meta`.\n *\n * The check is a three-way merge between:\n * - the original document (last-known remote, kept in\n * `workspaceStore.getOriginalDocument`),\n * - the editable workspace document (with the user's local edits),\n * - the freshly-fetched remote document.\n *\n * `registryCommitHash` is the hash advertised by the registry version\n * listing and acts as the pre-fetch cache key: when it matches the\n * previously stored `conflictCheckedAgainstHash`, we return the cached\n * result without touching the network. After a fresh fetch, the cache\n * is rewritten with the authoritative `versionSha` returned alongside\n * the document body so subsequent comparisons settle on the hash the\n * registry actually served.\n *\n * Writing to `x-scalar-registry-meta` does not flip the document's\n * `x-scalar-is-dirty` flag (the workspace store excludes registry meta from\n * the dirty tracker), so this side effect is safe to perform in the\n * background.\n */\nexport const checkVersionConflict = async ({\n workspaceStore,\n fetcher,\n documentName,\n namespace,\n slug,\n version,\n registryCommitHash,\n}: {\n workspaceStore: WorkspaceStore\n fetcher: ImportDocumentFromRegistry\n /** Name of the workspace document the cache lives on. */\n documentName: string\n namespace: string\n slug: string\n version: string\n /**\n * Commit hash advertised by the registry for this version. The conflict\n * cache is keyed on this hash; passing `undefined` skips the check.\n */\n registryCommitHash?: string\n}): Promise<CheckVersionConflictResult> => {\n if (!registryCommitHash) {\n return { ok: false, error: 'No registry commit hash available for this version.' }\n }\n\n const document = workspaceStore.workspace.documents[documentName]\n if (!document) {\n return { ok: false, error: `Document \"${documentName}\" is not loaded in the workspace.` }\n }\n\n const meta = document['x-scalar-registry-meta']\n\n // Cache hit - the previous check was performed against the registry hash\n // we are being asked about, so we can return the stored result without\n // touching the network.\n if (meta?.conflictCheckedAgainstHash === registryCommitHash && typeof meta?.hasConflict === 'boolean') {\n return { ok: true, hasConflict: meta.hasConflict, fromCache: true }\n }\n\n const original = workspaceStore.getOriginalDocument(documentName)\n if (!original) {\n return { ok: false, error: `Original document for \"${documentName}\" is unavailable.` }\n }\n\n const result = await fetcher({ namespace, slug, version })\n if (!result.ok) {\n return { ok: false, error: `Failed to fetch document: ${result.error || 'Unknown error'}` }\n }\n\n const hasConflict = detectDocumentConflicts({\n original: original as Record<string, unknown>,\n local: document as unknown as Record<string, unknown>,\n remote: result.data.document,\n })\n\n // Persist the cache on the document. The workspace store's dirty tracker\n // skips `x-scalar-registry-meta` writes, so this does not mark the\n // document as having local edits.\n const next = {\n ...(meta ?? { namespace, slug, version }),\n namespace,\n slug,\n version,\n conflictCheckedAgainstHash: result.data.versionSha,\n hasConflict,\n }\n document['x-scalar-registry-meta'] = next\n\n return { ok: true, hasConflict, fromCache: false }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,IAAa,uBAAuB,OAAO,EACzC,gBACA,SACA,cACA,WACA,MACA,SACA,yBAcyC;AACzC,KAAI,CAAC,mBACH,QAAO;EAAE,IAAI;EAAO,OAAO;EAAuD;CAGpF,MAAM,WAAW,eAAe,UAAU,UAAU;AACpD,KAAI,CAAC,SACH,QAAO;EAAE,IAAI;EAAO,OAAO,aAAa,aAAa;EAAoC;CAG3F,MAAM,OAAO,SAAS;AAKtB,KAAI,MAAM,+BAA+B,sBAAsB,OAAO,MAAM,gBAAgB,UAC1F,QAAO;EAAE,IAAI;EAAM,aAAa,KAAK;EAAa,WAAW;EAAM;CAGrE,MAAM,WAAW,eAAe,oBAAoB,aAAa;AACjE,KAAI,CAAC,SACH,QAAO;EAAE,IAAI;EAAO,OAAO,0BAA0B,aAAa;EAAoB;CAGxF,MAAM,SAAS,MAAM,QAAQ;EAAE;EAAW;EAAM;EAAS,CAAC;AAC1D,KAAI,CAAC,OAAO,GACV,QAAO;EAAE,IAAI;EAAO,OAAO,6BAA6B,OAAO,SAAS;EAAmB;CAG7F,MAAM,cAAc,wBAAwB;EAChC;EACV,OAAO;EACP,QAAQ,OAAO,KAAK;EACrB,CAAC;AAaF,UAAS,4BARI;EACX,GAAI,QAAQ;GAAE;GAAW;GAAM;GAAS;EACxC;EACA;EACA;EACA,4BAA4B,OAAO,KAAK;EACxC;EACD;AAGD,QAAO;EAAE,IAAI;EAAM;EAAa,WAAW;EAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ClientPlugin } from '@scalar/oas-utils/helpers';
|
|
2
2
|
import type { Theme } from '@scalar/themes';
|
|
3
|
-
import type {
|
|
3
|
+
import type { RegistryAdapter } from '../../../../v2/types/configuration';
|
|
4
4
|
import type { ClientLayout } from '../../../../v2/types/layout';
|
|
5
5
|
import type { ApiClientOptions } from '../../../../v2/types/options';
|
|
6
6
|
type CreateApiClientOptions = {
|
|
@@ -25,10 +25,13 @@ type CreateApiClientOptions = {
|
|
|
25
25
|
*/
|
|
26
26
|
fallbackThemeSlug?: () => string;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* Adapter wiring the API client up to an external registry. The
|
|
29
|
+
* adapter bundles the registry document list, the per-document fetch
|
|
30
|
+
* callback (used for syncing) and the publish callback used to create
|
|
31
|
+
* a brand-new registry version. Optional at the top level - omit to
|
|
32
|
+
* opt out of registry features entirely.
|
|
30
33
|
*/
|
|
31
|
-
|
|
34
|
+
registry?: RegistryAdapter;
|
|
32
35
|
/**
|
|
33
36
|
* Whether or not to send telemetry events.
|
|
34
37
|
*/
|
|
@@ -48,7 +51,7 @@ export declare const createAppRouter: (layout: CreateApiClientOptions["layout"])
|
|
|
48
51
|
/**
|
|
49
52
|
* Create the API Client with router and passes in the workspace store as a prop
|
|
50
53
|
*/
|
|
51
|
-
export declare const createApiClientApp: (el: HTMLElement | null, { layout, plugins, customThemes, fallbackThemeSlug,
|
|
54
|
+
export declare const createApiClientApp: (el: HTMLElement | null, { layout, plugins, customThemes, fallbackThemeSlug, registry, telemetry, options, }: CreateApiClientOptions) => Promise<{
|
|
52
55
|
app: import("vue").App<Element>;
|
|
53
56
|
state: import("../../../../v2/features/app/app-state.js").AppState;
|
|
54
57
|
} | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-api-client-app.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAO3C,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"create-api-client-app.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAO3C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAI1D,KAAK,sBAAsB,GAAG;IAC5B;;;;;OAKG;IACH,MAAM,EAAE,OAAO,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IACtC;;OAEG;IACH,OAAO,CAAC,EAAE,YAAY,EAAE,CAAA;IACxB;;OAEG;IACH,YAAY,CAAC,EAAE,KAAK,EAAE,CAAA;IACtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,CAAA;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kCAAkC;IAClC,OAAO,CAAC,EAAE,mBAAmB,CAAA;CAC9B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,IAAI,CAAC,gBAAgB,EAAE,aAAa,GAAG,mBAAmB,CAAC,CAAA;AAE7F;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,sBAAsB,CAAC,QAAQ,CAAC,gCAQvE,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAC7B,IAAI,WAAW,GAAG,IAAI,EACtB,qFAQG,sBAAsB;;;cAuC1B,CAAA"}
|
|
@@ -21,7 +21,7 @@ var createAppRouter = (layout) => {
|
|
|
21
21
|
/**
|
|
22
22
|
* Create the API Client with router and passes in the workspace store as a prop
|
|
23
23
|
*/
|
|
24
|
-
var createApiClientApp = async (el, { layout = "desktop", plugins, customThemes, fallbackThemeSlug,
|
|
24
|
+
var createApiClientApp = async (el, { layout = "desktop", plugins, customThemes, fallbackThemeSlug, registry, telemetry = true, options }) => {
|
|
25
25
|
const router = createAppRouter(layout);
|
|
26
26
|
const state = await createAppState({
|
|
27
27
|
router,
|
|
@@ -36,7 +36,7 @@ var createApiClientApp = async (el, { layout = "desktop", plugins, customThemes,
|
|
|
36
36
|
plugins,
|
|
37
37
|
getAppState: () => state,
|
|
38
38
|
getCommandPaletteState: () => commandPaletteState,
|
|
39
|
-
|
|
39
|
+
registry
|
|
40
40
|
});
|
|
41
41
|
app.use(router);
|
|
42
42
|
if (!el) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-api-client-app.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { Theme } from '@scalar/themes'\nimport { createApp } from 'vue'\nimport { createRouter as createVueRouter, createWebHashHistory, createWebHistory } from 'vue-router'\n\nimport App from '@/v2/features/app/App.vue'\nimport { createAppState } from '@/v2/features/app/app-state'\nimport { ROUTES } from '@/v2/features/app/helpers/routes'\nimport type {
|
|
1
|
+
{"version":3,"file":"create-api-client-app.js","names":[],"sources":["../../../../../src/v2/features/app/helpers/create-api-client-app.ts"],"sourcesContent":["import type { ClientPlugin } from '@scalar/oas-utils/helpers'\nimport type { Theme } from '@scalar/themes'\nimport { createApp } from 'vue'\nimport { createRouter as createVueRouter, createWebHashHistory, createWebHistory } from 'vue-router'\n\nimport App from '@/v2/features/app/App.vue'\nimport { createAppState } from '@/v2/features/app/app-state'\nimport { ROUTES } from '@/v2/features/app/helpers/routes'\nimport type { RegistryAdapter } from '@/v2/types/configuration'\nimport type { ClientLayout } from '@/v2/types/layout'\nimport type { ApiClientOptions } from '@/v2/types/options'\n\nimport { useCommandPaletteState } from '../../command-palette/hooks/use-command-palette-state'\n\ntype CreateApiClientOptions = {\n /**\n * The layout of the client, limited to web or desktop in app\n * @see {@link ClientLayout}\n *\n * @default 'desktop'\n */\n layout: Exclude<ClientLayout, 'modal'>\n /**\n * Api client plugins to include in the app\n */\n plugins?: ClientPlugin[]\n /**\n * Custom themes to include in the app\n */\n customThemes?: Theme[]\n /**\n * Fallback theme slug to use if no theme is selected for the workspace\n * @default 'default'\n */\n fallbackThemeSlug?: () => string\n /**\n * Adapter wiring the API client up to an external registry. The\n * adapter bundles the registry document list, the per-document fetch\n * callback (used for syncing) and the publish callback used to create\n * a brand-new registry version. Optional at the top level - omit to\n * opt out of registry features entirely.\n */\n registry?: RegistryAdapter\n /**\n * Whether or not to send telemetry events.\n */\n telemetry?: boolean\n /** Runtime behaviour overrides */\n options?: ApiClientAppOptions\n}\n\n/**\n * Runtime behaviour overrides shared between createApiClientApp and createAppState.\n * Derived from the canonical ApiClientOptions to guarantee structural compatibility.\n */\nexport type ApiClientAppOptions = Pick<ApiClientOptions, 'customFetch' | 'oauth2RedirectUri'>\n\n/**\n * Creates the appropriate router with the appropriate routes based on the layout\n */\nexport const createAppRouter = (layout: CreateApiClientOptions['layout']) => {\n // Web uses the standard HTML5 history API\n if (layout === 'web') {\n return createVueRouter({ history: createWebHistory(), routes: ROUTES })\n }\n\n // Electron app has to use the webHashHistory due to file routing\n return createVueRouter({ history: createWebHashHistory(), routes: ROUTES })\n}\n\n/**\n * Create the API Client with router and passes in the workspace store as a prop\n */\nexport const createApiClientApp = async (\n el: HTMLElement | null,\n {\n layout = 'desktop',\n plugins,\n customThemes,\n fallbackThemeSlug,\n registry,\n telemetry = true,\n options,\n }: CreateApiClientOptions,\n) => {\n // Add the router\n const router = createAppRouter(layout)\n const state = await createAppState({\n router,\n customThemes,\n fallbackThemeSlug,\n telemetryDefault: telemetry,\n options,\n })\n const commandPaletteState = useCommandPaletteState()\n\n // Pass in our initial props at the top level\n const app = createApp(App, {\n layout,\n plugins,\n getAppState: () => state,\n getCommandPaletteState: () => commandPaletteState,\n registry,\n })\n app.use(router)\n\n // Mount the vue app\n if (!el) {\n console.error(\n '[@scalar/api-client-modal] Could not create the API client.',\n 'Invalid HTML element provided.',\n 'Read more: https://github.com/scalar/scalar/tree/main/packages/api-client',\n )\n\n return\n }\n app.mount(el)\n\n return {\n app,\n state,\n }\n}\n"],"mappings":";;;;;;;;;;AA4DA,IAAa,mBAAmB,WAA6C;AAE3E,KAAI,WAAW,MACb,QAAO,aAAgB;EAAE,SAAS,kBAAkB;EAAE,QAAQ;EAAQ,CAAC;AAIzE,QAAO,aAAgB;EAAE,SAAS,sBAAsB;EAAE,QAAQ;EAAQ,CAAC;;;;;AAM7E,IAAa,qBAAqB,OAChC,IACA,EACE,SAAS,WACT,SACA,cACA,mBACA,UACA,YAAY,MACZ,cAEC;CAEH,MAAM,SAAS,gBAAgB,OAAO;CACtC,MAAM,QAAQ,MAAM,eAAe;EACjC;EACA;EACA;EACA,kBAAkB;EAClB;EACD,CAAC;CACF,MAAM,sBAAsB,wBAAwB;CAGpD,MAAM,MAAM,UAAU,aAAK;EACzB;EACA;EACA,mBAAmB;EACnB,8BAA8B;EAC9B;EACD,CAAC;AACF,KAAI,IAAI,OAAO;AAGf,KAAI,CAAC,IAAI;AACP,UAAQ,MACN,+DACA,kCACA,4EACD;AAED;;AAEF,KAAI,MAAM,GAAG;AAEb,QAAO;EACL;EACA;EACD"}
|
|
@@ -8,7 +8,7 @@ type LoadRegistryDocumentResult = {
|
|
|
8
8
|
ok: false;
|
|
9
9
|
error: string;
|
|
10
10
|
};
|
|
11
|
-
export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace, slug, version,
|
|
11
|
+
export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace, slug, version, }: {
|
|
12
12
|
workspaceStore: WorkspaceStore;
|
|
13
13
|
fetcher: ImportDocumentFromRegistry;
|
|
14
14
|
namespace: string;
|
|
@@ -19,15 +19,6 @@ export declare const loadRegistryDocument: ({ workspaceStore, fetcher, namespace
|
|
|
19
19
|
* not have a concrete version pinned for the document yet.
|
|
20
20
|
*/
|
|
21
21
|
version?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Commit hash the registry currently advertises for `version`. Callers
|
|
24
|
-
* already know this from the version listing (sidebar row, breadcrumb
|
|
25
|
-
* picker, etc.) so we accept it directly rather than parsing it back
|
|
26
|
-
* out of the fetched document. When provided it is persisted on
|
|
27
|
-
* `x-scalar-registry-meta.commitHash` so subsequent refreshes can
|
|
28
|
-
* detect drift and surface upstream changes.
|
|
29
|
-
*/
|
|
30
|
-
commitHash?: string;
|
|
31
22
|
}) => Promise<LoadRegistryDocumentResult>;
|
|
32
23
|
export {};
|
|
33
24
|
//# sourceMappingURL=load-registry-document.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-registry-document.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAGpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,iFAAiF;AACjF,KAAK,0BAA0B,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnG,eAAO,MAAM,oBAAoB,GAAU,
|
|
1
|
+
{"version":3,"file":"load-registry-document.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/app/helpers/load-registry-document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AAGpE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAA;AAE1E,iFAAiF;AACjF,KAAK,0BAA0B,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEnG,eAAO,MAAM,oBAAoB,GAAU,wDAMxC;IACD,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,0BAA0B,CAAA;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,KAAG,OAAO,CAAC,0BAA0B,CA6DrC,CAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { generateUniqueSlug } from "../../command-palette/helpers/generate-unique-slug.js";
|
|
2
2
|
import { coerce, object, string } from "@scalar/validation";
|
|
3
3
|
//#region src/v2/features/app/helpers/load-registry-document.ts
|
|
4
|
-
var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug, version = "latest"
|
|
4
|
+
var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug, version = "latest" }) => {
|
|
5
5
|
const documents = workspaceStore.workspace.documents;
|
|
6
6
|
const existing = Object.entries(documents).find(([, doc]) => {
|
|
7
7
|
const meta = doc?.["x-scalar-registry-meta"];
|
|
@@ -18,21 +18,22 @@ var loadRegistryDocument = async ({ workspaceStore, fetcher, namespace, slug, ve
|
|
|
18
18
|
});
|
|
19
19
|
if (!result.ok) return {
|
|
20
20
|
ok: false,
|
|
21
|
-
error: `Failed to fetch document: ${result.
|
|
21
|
+
error: `Failed to fetch document: ${result.message ?? result.error}`
|
|
22
22
|
};
|
|
23
|
-
const
|
|
23
|
+
const { document, versionSha } = result.data;
|
|
24
|
+
const documentName = await generateUniqueSlug(`${coerce(object({ info: object({ title: string() }) }), document).info.title.trim() || slug}-${version}`, new Set(Object.keys(documents)));
|
|
24
25
|
if (!documentName) return {
|
|
25
26
|
ok: false,
|
|
26
27
|
error: "Failed to generate a unique name for the document"
|
|
27
28
|
};
|
|
28
29
|
await workspaceStore.addDocument({
|
|
29
30
|
name: documentName,
|
|
30
|
-
document
|
|
31
|
+
document,
|
|
31
32
|
meta: { "x-scalar-registry-meta": {
|
|
32
33
|
namespace,
|
|
33
34
|
slug,
|
|
34
35
|
version,
|
|
35
|
-
...
|
|
36
|
+
...versionSha ? { commitHash: versionSha } : {}
|
|
36
37
|
} }
|
|
37
38
|
});
|
|
38
39
|
return {
|