@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.
Files changed (126) hide show
  1. package/CHANGELOG.md +20 -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 +3917 -4765
  6. package/dist/styles/tailwind.config.css +20 -0
  7. package/dist/styles/utilities.css +45 -0
  8. package/dist/v2/blocks/operation-block/OperationBlock.vue.d.ts.map +1 -1
  9. package/dist/v2/blocks/operation-block/OperationBlock.vue.js.map +1 -1
  10. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js +1 -0
  11. package/dist/v2/blocks/operation-block/OperationBlock.vue.script.js.map +1 -1
  12. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.d.ts.map +1 -1
  13. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js +1 -1
  14. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.js.map +1 -1
  15. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js +1 -1
  16. package/dist/v2/blocks/operation-code-sample/components/OperationCodeSample.vue.script.js.map +1 -1
  17. package/dist/v2/components/data-table/DataTableInput.vue.d.ts +1 -1
  18. package/dist/v2/components/data-table/DataTableInput.vue.d.ts.map +1 -1
  19. package/dist/v2/constants.js +1 -1
  20. package/dist/v2/features/app/App.vue.d.ts +15 -31
  21. package/dist/v2/features/app/App.vue.d.ts.map +1 -1
  22. package/dist/v2/features/app/App.vue.js.map +1 -1
  23. package/dist/v2/features/app/App.vue.script.js +107 -28
  24. package/dist/v2/features/app/App.vue.script.js.map +1 -1
  25. package/dist/v2/features/app/app-events.js +1 -1
  26. package/dist/v2/features/app/app-events.js.map +1 -1
  27. package/dist/v2/features/app/app-state.d.ts +10 -14
  28. package/dist/v2/features/app/app-state.d.ts.map +1 -1
  29. package/dist/v2/features/app/app-state.js +54 -22
  30. package/dist/v2/features/app/app-state.js.map +1 -1
  31. package/dist/v2/features/app/components/AppHeader.vue.d.ts.map +1 -1
  32. package/dist/v2/features/app/components/AppHeader.vue.js.map +1 -1
  33. package/dist/v2/features/app/components/AppHeader.vue.script.js +1 -1
  34. package/dist/v2/features/app/components/AppHeader.vue.script.js.map +1 -1
  35. package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts +32 -0
  36. package/dist/v2/features/app/components/AppHeaderActions.vue.d.ts.map +1 -0
  37. package/dist/v2/features/app/components/AppHeaderActions.vue.js +7 -0
  38. package/dist/v2/features/app/components/AppHeaderActions.vue.js.map +1 -0
  39. package/dist/v2/features/app/components/AppHeaderActions.vue.script.js +170 -0
  40. package/dist/v2/features/app/components/AppHeaderActions.vue.script.js.map +1 -0
  41. package/dist/v2/features/app/components/AppSidebar.vue.d.ts +2 -3
  42. package/dist/v2/features/app/components/AppSidebar.vue.d.ts.map +1 -1
  43. package/dist/v2/features/app/components/AppSidebar.vue.js +1 -1
  44. package/dist/v2/features/app/components/AppSidebar.vue.js.map +1 -1
  45. package/dist/v2/features/app/components/AppSidebar.vue.script.js +1 -2
  46. package/dist/v2/features/app/components/AppSidebar.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 +3 -34
  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/CommandPaletteOpenApiDocument.vue.d.ts.map +1 -1
  103. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.js.map +1 -1
  104. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js +25 -9
  105. package/dist/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue.script.js.map +1 -1
  106. package/dist/v2/features/command-palette/helpers/generate-unique-slug.d.ts.map +1 -1
  107. package/dist/v2/features/command-palette/helpers/generate-unique-slug.js +2 -2
  108. package/dist/v2/features/command-palette/helpers/generate-unique-slug.js.map +1 -1
  109. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.d.ts.map +1 -1
  110. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js +5 -5
  111. package/dist/v2/features/editor/hooks/use-three-way-merge-editor.js.map +1 -1
  112. package/dist/v2/types/configuration.d.ts +273 -7
  113. package/dist/v2/types/configuration.d.ts.map +1 -1
  114. package/dist/vue-styles.css +1389 -0
  115. package/package.json +22 -15
  116. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js +0 -7
  117. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.js.map +0 -1
  118. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js +0 -51
  119. package/dist/v2/features/app/components/DocumentSyncIndicator.vue.script.js.map +0 -1
  120. package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.d.ts.map +0 -1
  121. package/dist/v2/features/collection/components/SyncConflictResolutionEditor.vue.script.js.map +0 -1
  122. package/dist/v2/helpers/slugify.d.ts +0 -9
  123. package/dist/v2/helpers/slugify.d.ts.map +0 -1
  124. package/dist/v2/helpers/slugify.js +0 -15
  125. package/dist/v2/helpers/slugify.js.map +0 -1
  126. /package/dist/v2/features/{collection → app}/components/SyncConflictResolutionEditor.vue.d.ts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentCollection.vue.script.js","names":[],"sources":["../../../../src/v2/features/collection/DocumentCollection.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Document Collection Page\n *\n * Displays primary document editing and viewing interface, enabling users to:\n * - Choose a document icon\n * - Edit the document title\n * - Navigate among Overview, Servers, Authentication, Environment, Cookies, and Settings tabs\n */\nexport default {\n name: 'DocumentCollection',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport {\n ScalarButton,\n ScalarModal,\n ScalarSavePrompt,\n useLoadingState,\n useModal,\n} from '@scalar/components'\nimport {\n ScalarIconCloudArrowDown,\n ScalarIconDownload,\n ScalarIconFloppyDisk,\n ScalarIconSpinner,\n ScalarIconWarning,\n} from '@scalar/icons'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport { apply, type Difference, type merge } from '@scalar/json-magic/diff'\nimport { useToasts } from '@scalar/use-toasts'\nimport { deepClone } from '@scalar/workspace-store/helpers/deep-clone'\nimport { computed, ref } from 'vue'\nimport { RouterView } from 'vue-router'\n\nimport IconSelector from '@/components/IconSelector.vue'\nimport type { RouteProps } from '@/v2/features/app/helpers/routes'\nimport { downloadAsFile } from '@/v2/helpers/download-document'\n\nimport LabelInput from './components/LabelInput.vue'\nimport SyncConflictResolutionEditor from './components/SyncConflictResolutionEditor.vue'\nimport Tabs from './components/Tabs.vue'\n\nconst props = defineProps<RouteProps>()\n\n/** Snag the title from the info object */\nconst title = computed(() => props.document?.info?.title ?? '')\n\n/** Default to the folder icon */\nconst icon = computed(\n () => props.document?.['x-scalar-icon'] || 'interface-content-folder',\n)\n\nconst syncModal = useModal()\nconst dirtyBeforeSyncModal = useModal()\n\nconst isDocumentDirty = computed(\n () => props.document?.['x-scalar-is-dirty'] === true,\n)\n\nconst saveLoader = useLoadingState()\n\nconst documentSourceUrl = computed(\n () => props.document?.['x-scalar-original-source-url'] as string | undefined,\n)\n\nconst documentRegistryMeta = computed(\n () =>\n props.document?.['x-scalar-registry-meta'] as\n | {\n namespace: string\n slug: string\n version: string\n commitHash?: string\n }\n | undefined,\n)\n\n/** Show Sync when the document has a source URL or registry meta (registry can be used if fetchRegistryDocument is set). */\nconst canShowSyncButton = computed(\n () =>\n documentSourceUrl.value !== undefined ||\n documentRegistryMeta.value !== undefined,\n)\n\nconst { toast } = useToasts()\n\nconst undoChanges = () => {\n props.workspaceStore.revertDocumentChanges(props.documentSlug)\n}\n\nconst saveChanges = async () => {\n saveLoader.start()\n const res = await props.workspaceStore.saveDocument(props.documentSlug)\n await (res ? saveLoader.validate() : saveLoader.invalidate({ persist: true }))\n}\n\n/** Downloads the document as a JSON file using the last saved state. */\nconst downloadDocument = () => {\n const content = props.workspaceStore.exportDocument(\n props.documentSlug,\n 'json',\n false,\n )\n if (!content) return\n const baseName = title.value.replace(/[^\\w\\s-]/g, '').trim() || 'document'\n downloadAsFile(content, `${baseName}.json`)\n}\n\nconst handleSaveThenCloseDirtyModal = async () => {\n await props.workspaceStore.saveDocument(props.documentSlug)\n dirtyBeforeSyncModal.hide()\n await handleSyncFlow()\n}\n\nconst handleDiscardThenCloseDirtyModal = async () => {\n await props.workspaceStore.revertDocumentChanges(props.documentSlug)\n dirtyBeforeSyncModal.hide()\n await handleSyncFlow()\n}\n\nconst isSyncInProgress = ref(false)\n\nconst rebaseResult = ref<{\n originalDocument: Record<string, unknown>\n resolvedDocument: Record<string, unknown>\n conflicts: ReturnType<typeof merge>['conflicts']\n applyChanges: (\n applyChangesInput:\n | {\n resolvedConflicts: Difference<unknown>[]\n }\n | {\n resolvedDocument: Record<string, unknown>\n },\n ) => Promise<void>\n} | null>(null)\n\n/**\n * Resolves the source for syncing. Registry meta has priority over x-scalar-original-source-url\n * when fetchRegistryDocument is provided. Returns either a URL or the full document object.\n */\nconst resolveSyncInput = async (): Promise<\n { url: string } | { document: Record<string, unknown> } | null\n> => {\n const registryMeta = documentRegistryMeta.value\n if (registryMeta && props.fetchRegistryDocument) {\n try {\n const result = await props.fetchRegistryDocument({\n namespace: registryMeta.namespace,\n slug: registryMeta.slug,\n version: registryMeta.version,\n })\n if (!result.ok) {\n toast(result.error, 'error')\n return null\n }\n return { document: result.data }\n } catch (err) {\n toast('Failed to resolve document from registry', 'error')\n return null\n }\n }\n const url = documentSourceUrl.value\n if (url) {\n return { url }\n }\n return null\n}\n\n/**\n * Handles actions to perform when synchronization is complete.\n * Hides the sync modal, resets the sync progress flag, and emits the\n * 'hooks:on:rebase:document:complete' event with document metadata.\n */\nconst onSyncComplete = () => {\n syncModal.hide()\n isSyncInProgress.value = false\n // Display the toast to show that the sync is complete\n toast(\n 'Your document has been rebased with the latest version from the source.',\n 'info',\n )\n // Emit the event to notify other components that the sync is complete\n props.eventBus.emit('hooks:on:rebase:document:complete', {\n meta: {\n documentName: props.documentSlug,\n },\n })\n}\n\n/**\n * Handles errors that occur during synchronization.\n * If an error string is provided, it displays the error via toast.\n * Always resets the sync progress flag.\n */\nconst onSyncError = (error: string | null) => {\n if (error !== null) {\n toast(error, 'error')\n }\n isSyncInProgress.value = false\n}\n\n/**\n * Handles the synchronization flow for a document.\n * Checks for unsaved changes, resolves source (registry over URL),\n * initiates rebasing, handles conflicts, and emits completion events.\n * If conflicts are detected, a modal dialog is shown for user resolution.\n */\nconst handleSyncFlow = async () => {\n if (isDocumentDirty.value) {\n dirtyBeforeSyncModal.show()\n return\n }\n\n if (isSyncInProgress.value) {\n return\n }\n\n isSyncInProgress.value = true\n\n const input = await resolveSyncInput()\n if (!input) {\n onSyncError(null)\n return\n }\n\n const result = await props.workspaceStore.rebaseDocument({\n name: props.documentSlug,\n ...input,\n })\n\n if (result?.ok) {\n const originalDocument =\n props.workspaceStore.getOriginalDocument(props.documentSlug) ?? {}\n rebaseResult.value = {\n conflicts: result.conflicts,\n applyChanges: result.applyChanges,\n resolvedDocument: apply(deepClone(originalDocument), result.changes),\n originalDocument,\n }\n\n if (rebaseResult.value.conflicts.length > 0) {\n syncModal.show()\n } else {\n // If there is no conflict just rebase immediately\n await rebaseResult.value?.applyChanges({\n resolvedDocument: rebaseResult.value.resolvedDocument,\n })\n onSyncComplete()\n }\n } else if (result?.ok === false && result.type === 'NO_CHANGES_DETECTED') {\n // Emit the event either way even if there was no need to rebase the document\n onSyncComplete()\n } else {\n onSyncError('Failed to sync document')\n }\n}\n\n/*\n * Handles applying changes to the current document after conflict resolution.\n * Emits a completion event and hides the sync modal dialog.\n */\nconst handleApplyChanges = async ({\n resolvedDocument,\n}: {\n resolvedDocument: Record<string, unknown>\n}) => {\n await rebaseResult.value?.applyChanges({ resolvedDocument })\n props.eventBus.emit('hooks:on:rebase:document:complete', {\n meta: {\n documentName: props.documentSlug,\n },\n })\n syncModal.hide()\n}\n\n/**\n * Resets sync state when the sync conflict modal is closed (dismissed or after\n * applying changes). Ensures the Sync button is re-enabled and conflict state\n * is cleared.\n */\nconst onSyncModalClose = () => {\n isSyncInProgress.value = false\n rebaseResult.value = null\n}\n</script>\n\n<template>\n <div class=\"custom-scroll h-full\">\n <div\n v-if=\"document\"\n class=\"md:max-w-content w-full px-3 md:mx-auto\">\n <!-- Header -->\n <div\n :aria-label=\"`title: ${title}`\"\n class=\"md:max-w-content mx-auto flex h-fit w-full flex-col gap-2 pt-14 pb-3 md:pt-6\">\n <ScalarSavePrompt\n v-model=\"isDocumentDirty\"\n class=\"w-content-padded-4 max-w-full-padded-4 absolute\"\n :loader=\"saveLoader\"\n @discard=\"undoChanges\"\n @save=\"saveChanges\" />\n <div class=\"flex flex-row items-center justify-between gap-2\">\n <div class=\"flex min-w-0 items-center gap-2\">\n <IconSelector\n :modelValue=\"icon\"\n placement=\"bottom-start\"\n @update:modelValue=\"\n (icon) => eventBus.emit('document:update:icon', icon)\n \">\n <ScalarButton\n class=\"hover:bg-b-2 aspect-square h-7 w-7 cursor-pointer rounded border border-transparent p-0 hover:border-inherit\"\n variant=\"ghost\">\n <LibraryIcon\n class=\"text-c-2 size-5\"\n :src=\"icon\"\n stroke-width=\"2\" />\n </ScalarButton>\n </IconSelector>\n\n <div class=\"group relative ml-1.25 min-w-0\">\n <LabelInput\n class=\"text-xl font-bold\"\n inputId=\"documentName\"\n :modelValue=\"title\"\n @update:modelValue=\"\n (title) => eventBus.emit('document:update:info', { title })\n \" />\n </div>\n </div>\n\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex shrink-0 items-center gap-2\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"downloadDocument\">\n <ScalarIconDownload\n size=\"sm\"\n thickness=\"1.5\" />\n <span>Download document</span>\n </ScalarButton>\n\n <ScalarButton\n v-if=\"canShowSyncButton\"\n class=\"text-c-2 hover:text-c-1 shrink-0 gap-1.5\"\n data-testid=\"document-sync-button\"\n :disabled=\"isSyncInProgress\"\n size=\"xs\"\n :title=\"'Pull the latest version from the document source and merge with your local copy. Save your changes first if you have unsaved edits.'\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"handleSyncFlow\">\n <ScalarIconSpinner\n v-if=\"isSyncInProgress\"\n class=\"size-3.5 animate-spin\"\n size=\"sm\" />\n <ScalarIconCloudArrowDown\n v-else\n class=\"size-3.5\"\n size=\"sm\"\n thickness=\"1.5\" />\n <span>Sync from source</span>\n </ScalarButton>\n </div>\n </div>\n\n <!-- Tabs -->\n <Tabs type=\"document\" />\n\n <!-- Router views -->\n <div class=\"px-1.5 pt-8 pb-20\">\n <RouterView v-slot=\"{ Component }\">\n <component\n :is=\"Component\"\n v-bind=\"props\"\n collectionType=\"document\" />\n </RouterView>\n </div>\n </div>\n\n <!-- Document not found -->\n <div\n v-else\n class=\"flex w-full flex-1 items-center justify-center\">\n <div class=\"flex h-full flex-col items-center justify-center\">\n <h1 class=\"text-2xl font-bold\">Document not found</h1>\n <p class=\"text-gray-500\">\n The document you are looking for does not exist.\n </p>\n </div>\n </div>\n </div>\n <ScalarModal\n bodyClass=\"border-t-0 rounded-t-lg flex flex-col gap-5\"\n size=\"xs\"\n :state=\"dirtyBeforeSyncModal\"\n title=\"Sync requires saved document\"\n @close=\"dirtyBeforeSyncModal.hide()\">\n <div class=\"flex flex-col gap-5\">\n <div class=\"flex gap-3\">\n <div\n aria-hidden=\"true\"\n class=\"bg-b-3 text-c-2 flex size-10 shrink-0 items-center justify-center rounded-lg\">\n <ScalarIconWarning class=\"text-yellow size-5\" />\n </div>\n <div class=\"min-w-0 flex-1 space-y-1\">\n <p class=\"text-c-1 text-sm leading-snug font-medium\">\n You have unsaved changes\n </p>\n <p class=\"text-c-2 text-sm leading-relaxed\">\n Save your work to keep changes, or discard to revert to the last\n saved version. Then you can sync with the source.\n </p>\n </div>\n </div>\n <div class=\"flex flex-wrap items-center justify-end gap-2 border-t pt-4\">\n <ScalarButton\n size=\"sm\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"dirtyBeforeSyncModal.hide()\">\n Cancel\n </ScalarButton>\n <ScalarButton\n size=\"sm\"\n type=\"button\"\n variant=\"outlined\"\n @click=\"handleDiscardThenCloseDirtyModal\">\n Discard changes\n </ScalarButton>\n <ScalarButton\n class=\"flex items-center gap-2\"\n size=\"sm\"\n type=\"button\"\n variant=\"solid\"\n @click=\"handleSaveThenCloseDirtyModal\">\n <ScalarIconFloppyDisk\n size=\"sm\"\n thickness=\"1.5\" />\n Save and continue\n </ScalarButton>\n </div>\n </div>\n </ScalarModal>\n <ScalarModal\n v-if=\"rebaseResult\"\n bodyClass=\"sync-conflict-modal-root flex h-dvh flex-col p-4\"\n maxWidth=\"calc(100dvw - 32px)\"\n size=\"full\"\n :state=\"syncModal\"\n @close=\"onSyncModalClose\">\n <div class=\"flex h-full w-full flex-col gap-4 overflow-hidden\">\n <SyncConflictResolutionEditor\n :baseDocument=\"rebaseResult.originalDocument\"\n :conflicts=\"rebaseResult.conflicts\"\n :resolvedDocument=\"rebaseResult.resolvedDocument\"\n @applyChanges=\"(payload) => handleApplyChanges(payload)\" />\n </div>\n </ScalarModal>\n</template>\n\n<style>\n.full-size-styles:has(.sync-conflict-modal-root) {\n width: 100dvw !important;\n max-width: 100dvw !important;\n border-right: none !important;\n}\n\n.full-size-styles:has(.sync-conflict-modal-root)::after {\n display: none;\n}\n</style>\n"],"mappings":"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"}
1
+ {"version":3,"file":"DocumentCollection.vue.script.js","names":[],"sources":["../../../../src/v2/features/collection/DocumentCollection.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Document Collection Page\n *\n * Displays primary document editing and viewing interface, enabling users to:\n * - Choose a document icon\n * - Edit the document title\n * - Navigate among Overview, Servers, Authentication, Environment, Cookies, and Settings tabs\n */\nexport default {\n name: 'DocumentCollection',\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { ScalarIconDownload } from '@scalar/icons'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport { computed } from 'vue'\nimport { RouterView } from 'vue-router'\n\nimport IconSelector from '@/components/IconSelector.vue'\nimport type { RouteProps } from '@/v2/features/app/helpers/routes'\nimport { downloadAsFile } from '@/v2/helpers/download-document'\n\nimport LabelInput from './components/LabelInput.vue'\nimport Tabs from './components/Tabs.vue'\n\nconst props = defineProps<RouteProps>()\n\n/** Snag the title from the info object */\nconst title = computed(() => props.document?.info?.title ?? '')\n\n/** Default to the folder icon */\nconst icon = computed(\n () => props.document?.['x-scalar-icon'] || 'interface-content-folder',\n)\n\n/** Downloads the document as a JSON file using the last saved state. */\nconst downloadDocument = () => {\n const content = props.workspaceStore.exportDocument(\n props.documentSlug,\n 'json',\n false,\n )\n if (!content) return\n const baseName = title.value.replace(/[^\\w\\s-]/g, '').trim() || 'document'\n downloadAsFile(content, `${baseName}.json`)\n}\n</script>\n\n<template>\n <div class=\"custom-scroll h-full\">\n <div\n v-if=\"document\"\n class=\"md:max-w-content w-full px-3 md:mx-auto\">\n <!-- Header -->\n <div\n :aria-label=\"`title: ${title}`\"\n class=\"md:max-w-content mx-auto flex h-fit w-full flex-col gap-2 pt-14 pb-3 md:pt-6\">\n <div class=\"flex flex-row items-center justify-between gap-2\">\n <div class=\"flex min-w-0 items-center gap-2\">\n <IconSelector\n :modelValue=\"icon\"\n placement=\"bottom-start\"\n @update:modelValue=\"\n (icon) => eventBus.emit('document:update:icon', icon)\n \">\n <ScalarButton\n class=\"hover:bg-b-2 aspect-square h-7 w-7 cursor-pointer rounded border border-transparent p-0 hover:border-inherit\"\n variant=\"ghost\">\n <LibraryIcon\n class=\"text-c-2 size-5\"\n :src=\"icon\"\n stroke-width=\"2\" />\n </ScalarButton>\n </IconSelector>\n\n <div class=\"group relative ml-1.25 min-w-0\">\n <LabelInput\n class=\"text-xl font-bold\"\n inputId=\"documentName\"\n :modelValue=\"title\"\n @update:modelValue=\"\n (title) => eventBus.emit('document:update:info', { title })\n \" />\n </div>\n </div>\n\n <ScalarButton\n class=\"text-c-2 hover:text-c-1 flex shrink-0 items-center gap-2\"\n size=\"xs\"\n type=\"button\"\n variant=\"ghost\"\n @click=\"downloadDocument\">\n <ScalarIconDownload\n size=\"sm\"\n thickness=\"1.5\" />\n <span>Download document</span>\n </ScalarButton>\n </div>\n </div>\n\n <!-- Tabs -->\n <Tabs type=\"document\" />\n\n <!-- Router views -->\n <div class=\"px-1.5 pt-8 pb-20\">\n <RouterView v-slot=\"{ Component }\">\n <component\n :is=\"Component\"\n v-bind=\"props\"\n collectionType=\"document\" />\n </RouterView>\n </div>\n </div>\n\n <!-- Document not found -->\n <div\n v-else\n class=\"flex w-full flex-1 items-center justify-center\">\n <div class=\"flex h-full flex-col items-center justify-center\">\n <h1 class=\"text-2xl font-bold\">Document not found</h1>\n <p class=\"text-gray-500\">\n The document you are looking for does not exist.\n </p>\n </div>\n </div>\n </div>\n</template>\n\n<style>\n.full-size-styles:has(.sync-conflict-modal-root) {\n width: 100dvw !important;\n max-width: 100dvw !important;\n border-right: none !important;\n}\n\n.full-size-styles:has(.sync-conflict-modal-root)::after {\n display: none;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;CAUE,MAAM;;;;;;;;;;;;;;;;;;;;;;;EAkBR,MAAM,QAAQ;;EAGd,MAAM,QAAQ,eAAe,MAAM,UAAU,MAAM,SAAS,GAAE;;EAG9D,MAAM,OAAO,eACL,MAAM,WAAW,oBAAoB,2BAC7C;;EAGA,MAAM,yBAAyB;GAC7B,MAAM,UAAU,MAAM,eAAe,eACnC,MAAM,cACN,QACA,MACF;AACA,OAAI,CAAC,QAAS;AAEd,kBAAe,SAAS,GADP,MAAM,MAAM,QAAQ,aAAa,GAAG,CAAC,MAAM,IAAI,WAC5B,OAAM;;;uBAK1C,mBA4EM,OA5EN,YA4EM,CA1EI,QAAA,YAAA,WAAA,EADR,mBA8DM,OA9DN,YA8DM;IA1DJ,mBA4CM,OAAA;KA3CH,cAAU,UAAY,MAAA;KACvB,OAAM;QACN,mBAwCM,OAxCN,YAwCM,CAvCJ,mBA0BM,OA1BN,YA0BM,CAzBJ,YAce,sBAAA;KAbZ,YAAY,KAAA;KACb,WAAU;KACT,uBAAiB,OAAA,OAAA,OAAA,MAAoB,SAAS,QAAA,SAAS,KAAI,wBAAyB,KAAI;;4BAU1E,CAPf,YAOe,MAAA,aAAA,EAAA;MANb,OAAM;MACN,SAAQ;;6BAIa,CAHrB,YAGqB,MAAA,YAAA,EAAA;OAFnB,OAAM;OACL,KAAK,KAAA;OACN,gBAAa;;;;;2BAInB,mBAQM,OARN,YAQM,CAPJ,YAMM,oBAAA;KALJ,OAAM;KACN,SAAQ;KACP,YAAY,MAAA;KACZ,uBAAiB,OAAA,OAAA,OAAA,MAAsB,UAAU,QAAA,SAAS,KAAI,wBAAA,EAA2B,OAAK,CAAA;qCAMrG,YAUe,MAAA,aAAA,EAAA;KATb,OAAM;KACN,MAAK;KACL,MAAK;KACL,SAAQ;KACP,SAAO;;4BAGY,CAFpB,YAEoB,MAAA,mBAAA,EAAA;MADlB,MAAK;MACL,WAAU;mCACZ,mBAA8B,QAAA,MAAxB,qBAAiB,GAAA,EAAA,CAAA;;;IAM7B,YAAwB,cAAA,EAAlB,MAAK,YAAU,CAAA;IAGrB,mBAOM,OAPN,YAOM,CANJ,YAKa,MAAA,WAAA,EAAA,MAAA;uBADmB,EAJV,gBAAS,EAAA,WAAA,EAC7B,YAG8B,wBAFvB,UAAS,EADhB,WAEU,OAAK,EACb,gBAAe,YAAU,CAAA,EAAA,MAAA,GAAA,EAAA,CAAA;;;uBAMjC,mBASM,OATN,YASM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CANJ,mBAKM,OAAA,EALD,OAAM,oDAAkD,EAAA,CAC3D,mBAAsD,MAAA,EAAlD,OAAM,sBAAoB,EAAC,qBAAkB,EACjD,mBAEI,KAAA,EAFD,OAAM,iBAAe,EAAC,qDAEzB,CAAA,EAAA,GAAA,CAAA,EAAA,CAAA,EAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"CommandPaletteOpenApiDocument.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"names":[],"mappings":"AAwIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAUvE;;;;;;;;;;;;;;GAcG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,2DAA2D;oBAC3C,cAAc;IAC9B,sDAAsD;cAC5C,iBAAiB;;;;;IAH3B,2DAA2D;oBAC3C,cAAc;IAC9B,sDAAsD;cAC5C,iBAAiB;;;;kFAwMzB,CAAC"}
1
+ {"version":3,"file":"CommandPaletteOpenApiDocument.vue.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"names":[],"mappings":"AA4JA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAUvE;;;;;;;;;;;;;;GAcG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AAGzC,QAAA,MAAM,YAAY;IAEhB,2DAA2D;oBAC3C,cAAc;IAC9B,sDAAsD;cAC5C,iBAAiB;;;;;IAH3B,2DAA2D;oBAC3C,cAAc;IAC9B,sDAAsD;cAC5C,iBAAiB;;;;kFAiOzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CommandPaletteOpenApiDocument.vue.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport IconSelector from '@/components/IconSelector.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus } = defineProps<{\n /** The workspace store for accessing existing documents */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting document creation events */\n eventBus: WorkspaceEventBus\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the document is created successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst router = useRouter()\n\nconst documentName = ref('')\nconst documentNameTrimmed = computed(() => documentName.value.trim())\n\n/** Default icon for new documents (folder icon) */\nconst documentIcon = ref('interface-content-folder')\n\n/**\n * Check if the form should be disabled.\n * Disabled when the name is empty or when a document with that name already exists.\n */\nconst isDisabled = computed<boolean>(() => {\n if (!documentNameTrimmed.value) {\n return true\n }\n\n /** Prevent duplicate document names in the workspace */\n if (\n workspaceStore.workspace.documents[documentNameTrimmed.value] !== undefined\n ) {\n return true\n }\n\n return false\n})\n\n/** Handle form submission to create a new document */\nconst handleSubmit = (): void => {\n if (isDisabled.value) {\n return\n }\n\n eventBus.emit('document:create:empty-document', {\n name: documentNameTrimmed.value,\n icon: documentIcon.value,\n callback: (success) => {\n if (success) {\n router.push({\n name: 'document.overview',\n params: {\n documentSlug: documentNameTrimmed.value,\n },\n })\n }\n },\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n emit('back', event)\n}\n</script>\n\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <CommandActionInput\n v-model=\"documentName\"\n label=\"Document Name\"\n placeholder=\"Document Name\"\n @delete=\"handleBack\" />\n\n <!-- Icon selector for choosing document icon -->\n <template #options>\n <IconSelector\n v-model=\"documentIcon\"\n placement=\"bottom-start\">\n <ScalarButton\n class=\"aspect-square h-auto px-0\"\n variant=\"outlined\">\n <LibraryIcon\n class=\"text-c-2 size-4 stroke-[1.75]\"\n :src=\"documentIcon\" />\n </ScalarButton>\n </IconSelector>\n </template>\n\n <template #submit>Create Document</template>\n </CommandActionForm>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"CommandPaletteOpenApiDocument.vue.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport IconSelector from '@/components/IconSelector.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus } = defineProps<{\n /** The workspace store for accessing existing documents */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting document creation events */\n eventBus: WorkspaceEventBus\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the document is created successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst router = useRouter()\n\nconst documentName = ref('')\nconst documentNameTrimmed = computed(() => documentName.value.trim())\n\n/** Default icon for new documents (folder icon) */\nconst documentIcon = ref('interface-content-folder')\n\n/**\n * Validation message surfaced under the input.\n *\n * Resolves to `null` when the form is valid; otherwise to a human-readable\n * reason the user can act on. Empty input is the default state so we keep\n * the field free of error styling - `isDisabled` still blocks submission\n * there, matching the `CreateVersionModal` pattern.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (!documentNameTrimmed.value) {\n return null\n }\n\n if (\n workspaceStore.workspace.documents[documentNameTrimmed.value] !== undefined\n ) {\n return `A document named \"${documentNameTrimmed.value}\" already exists. Try a different name.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while the input is empty or fails validation. We keep the\n * button disabled (rather than letting the user fire a no-op submit) because\n * the inline `errorMessage` already explains the fix out loud.\n */\nconst isDisabled = computed<boolean>(\n () => !documentNameTrimmed.value || errorMessage.value !== null,\n)\n\n/** Handle form submission to create a new document */\nconst handleSubmit = (): void => {\n if (isDisabled.value) {\n return\n }\n\n eventBus.emit('document:create:empty-document', {\n name: documentNameTrimmed.value,\n icon: documentIcon.value,\n callback: (success) => {\n if (success) {\n router.push({\n name: 'document.overview',\n params: {\n documentSlug: documentNameTrimmed.value,\n },\n })\n }\n },\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n emit('back', event)\n}\n</script>\n\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <CommandActionInput\n v-model=\"documentName\"\n label=\"Document Name\"\n placeholder=\"Document Name\"\n @delete=\"handleBack\" />\n\n <p\n v-if=\"errorMessage\"\n class=\"text-red px-2 pb-1 text-xs\"\n data-testid=\"command-palette-document-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Icon selector for choosing document icon -->\n <template #options>\n <IconSelector\n v-model=\"documentIcon\"\n placement=\"bottom-start\">\n <ScalarButton\n class=\"aspect-square h-auto px-0\"\n variant=\"outlined\">\n <LibraryIcon\n class=\"text-c-2 size-4 stroke-[1.75]\"\n :src=\"documentIcon\" />\n </ScalarButton>\n </IconSelector>\n </template>\n\n <template #submit>Create Document</template>\n </CommandActionForm>\n</template>\n"],"mappings":""}
@@ -1,10 +1,17 @@
1
1
  import CommandActionForm_default from "./CommandActionForm.vue.js";
2
2
  import CommandActionInput_default from "./CommandActionInput.vue.js";
3
3
  import IconSelector_default from "../../../../components/IconSelector.vue.js";
4
- import { computed, createBlock, createTextVNode, createVNode, defineComponent, openBlock, ref, unref, withCtx } from "vue";
4
+ import { computed, createBlock, createCommentVNode, createElementBlock, createTextVNode, createVNode, defineComponent, openBlock, ref, toDisplayString, unref, withCtx } from "vue";
5
5
  import { ScalarButton } from "@scalar/components";
6
6
  import { useRouter } from "vue-router";
7
7
  import { LibraryIcon } from "@scalar/icons/library";
8
+ //#region src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue?vue&type=script&setup=true&lang.ts
9
+ var _hoisted_1 = {
10
+ key: 0,
11
+ class: "text-red px-2 pb-1 text-xs",
12
+ "data-testid": "command-palette-document-error",
13
+ role: "alert"
14
+ };
8
15
  var CommandPaletteOpenApiDocument_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
9
16
  __name: "CommandPaletteOpenApiDocument",
10
17
  props: {
@@ -20,15 +27,24 @@ var CommandPaletteOpenApiDocument_vue_vue_type_script_setup_true_lang_default =
20
27
  /** Default icon for new documents (folder icon) */
21
28
  const documentIcon = ref("interface-content-folder");
22
29
  /**
23
- * Check if the form should be disabled.
24
- * Disabled when the name is empty or when a document with that name already exists.
30
+ * Validation message surfaced under the input.
31
+ *
32
+ * Resolves to `null` when the form is valid; otherwise to a human-readable
33
+ * reason the user can act on. Empty input is the default state so we keep
34
+ * the field free of error styling - `isDisabled` still blocks submission
35
+ * there, matching the `CreateVersionModal` pattern.
25
36
  */
26
- const isDisabled = computed(() => {
27
- if (!documentNameTrimmed.value) return true;
28
- /** Prevent duplicate document names in the workspace */
29
- if (__props.workspaceStore.workspace.documents[documentNameTrimmed.value] !== void 0) return true;
30
- return false;
37
+ const errorMessage = computed(() => {
38
+ if (!documentNameTrimmed.value) return null;
39
+ if (__props.workspaceStore.workspace.documents[documentNameTrimmed.value] !== void 0) return `A document named "${documentNameTrimmed.value}" already exists. Try a different name.`;
40
+ return null;
31
41
  });
42
+ /**
43
+ * Submit is blocked while the input is empty or fails validation. We keep the
44
+ * button disabled (rather than letting the user fire a no-op submit) because
45
+ * the inline `errorMessage` already explains the fix out loud.
46
+ */
47
+ const isDisabled = computed(() => !documentNameTrimmed.value || errorMessage.value !== null);
32
48
  /** Handle form submission to create a new document */
33
49
  const handleSubmit = () => {
34
50
  if (isDisabled.value) return;
@@ -77,7 +93,7 @@ var CommandPaletteOpenApiDocument_vue_vue_type_script_setup_true_lang_default =
77
93
  label: "Document Name",
78
94
  placeholder: "Document Name",
79
95
  onDelete: handleBack
80
- }, null, 8, ["modelValue"])]),
96
+ }, null, 8, ["modelValue"]), errorMessage.value ? (openBlock(), createElementBlock("p", _hoisted_1, toDisplayString(errorMessage.value), 1)) : createCommentVNode("", true)]),
81
97
  _: 1
82
98
  }, 8, ["disabled"]);
83
99
  };
@@ -1 +1 @@
1
- {"version":3,"file":"CommandPaletteOpenApiDocument.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport IconSelector from '@/components/IconSelector.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus } = defineProps<{\n /** The workspace store for accessing existing documents */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting document creation events */\n eventBus: WorkspaceEventBus\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the document is created successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst router = useRouter()\n\nconst documentName = ref('')\nconst documentNameTrimmed = computed(() => documentName.value.trim())\n\n/** Default icon for new documents (folder icon) */\nconst documentIcon = ref('interface-content-folder')\n\n/**\n * Check if the form should be disabled.\n * Disabled when the name is empty or when a document with that name already exists.\n */\nconst isDisabled = computed<boolean>(() => {\n if (!documentNameTrimmed.value) {\n return true\n }\n\n /** Prevent duplicate document names in the workspace */\n if (\n workspaceStore.workspace.documents[documentNameTrimmed.value] !== undefined\n ) {\n return true\n }\n\n return false\n})\n\n/** Handle form submission to create a new document */\nconst handleSubmit = (): void => {\n if (isDisabled.value) {\n return\n }\n\n eventBus.emit('document:create:empty-document', {\n name: documentNameTrimmed.value,\n icon: documentIcon.value,\n callback: (success) => {\n if (success) {\n router.push({\n name: 'document.overview',\n params: {\n documentSlug: documentNameTrimmed.value,\n },\n })\n }\n },\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n emit('back', event)\n}\n</script>\n\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <CommandActionInput\n v-model=\"documentName\"\n label=\"Document Name\"\n placeholder=\"Document Name\"\n @delete=\"handleBack\" />\n\n <!-- Icon selector for choosing document icon -->\n <template #options>\n <IconSelector\n v-model=\"documentIcon\"\n placement=\"bottom-start\">\n <ScalarButton\n class=\"aspect-square h-auto px-0\"\n variant=\"outlined\">\n <LibraryIcon\n class=\"text-c-2 size-4 stroke-[1.75]\"\n :src=\"documentIcon\" />\n </ScalarButton>\n </IconSelector>\n </template>\n\n <template #submit>Create Document</template>\n </CommandActionForm>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;EAuCA,MAAM,OAAO;EAOb,MAAM,SAAS,WAAU;EAEzB,MAAM,eAAe,IAAI,GAAE;EAC3B,MAAM,sBAAsB,eAAe,aAAa,MAAM,MAAM,CAAA;;EAGpE,MAAM,eAAe,IAAI,2BAA0B;;;;;EAMnD,MAAM,aAAa,eAAwB;AACzC,OAAI,CAAC,oBAAoB,MACvB,QAAO;;AAIT,OACE,QAAA,eAAe,UAAU,UAAU,oBAAoB,WAAW,KAAA,EAElE,QAAO;AAGT,UAAO;IACR;;EAGD,MAAM,qBAA2B;AAC/B,OAAI,WAAW,MACb;AAGF,WAAA,SAAS,KAAK,kCAAkC;IAC9C,MAAM,oBAAoB;IAC1B,MAAM,aAAa;IACnB,WAAW,YAAY;AACrB,SAAI,QACF,QAAO,KAAK;MACV,MAAM;MACN,QAAQ,EACN,cAAc,oBAAoB,OACnC;MACF,CAAA;;IAGN,CAAA;AAED,QAAK,QAAO;;;EAId,MAAM,cAAc,UAA+B;AACjD,QAAK,QAAQ,MAAK;;;uBAKlB,YAyBoB,2BAAA;IAxBjB,UAAU,WAAA;IACV,UAAQ;;IAQE,SAAO,cAWD,CAVf,YAUe,sBAAA;iBATJ,aAAA;+EAAY,QAAA;KACrB,WAAU;;4BAOK,CANf,YAMe,MAAA,aAAA,EAAA;MALb,OAAM;MACN,SAAQ;;6BAGgB,CAFxB,YAEwB,MAAA,YAAA,EAAA;OADtB,OAAM;OACL,KAAK,aAAA;;;;;;IAKH,QAAM,cAAgB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAf,mBAAe,GAAA,CAAA,EAAA,CAAA;2BAjBR,CAJzB,YAIyB,4BAAA;iBAHd,aAAA;+EAAY,QAAA;KACrB,OAAM;KACN,aAAY;KACX,UAAQ"}
1
+ {"version":3,"file":"CommandPaletteOpenApiDocument.vue.script.js","names":[],"sources":["../../../../../src/v2/features/command-palette/components/CommandPaletteOpenApiDocument.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Command Palette OpenAPI Document Component\n *\n * Provides a form for creating a new empty document in the workspace.\n * Users can name the document and select an icon before creation.\n * Validates that the name is not empty and not already in use.\n *\n * @example\n * <CommandPaletteOpenApiDocument\n * :workspaceStore=\"workspaceStore\"\n * :eventBus=\"eventBus\"\n * @close=\"handleClose\"\n * @back=\"handleBack\"\n * />\n */\nexport default {}\n</script>\n\n<script setup lang=\"ts\">\nimport { ScalarButton } from '@scalar/components'\nimport { LibraryIcon } from '@scalar/icons/library'\nimport type { WorkspaceStore } from '@scalar/workspace-store/client'\nimport type { WorkspaceEventBus } from '@scalar/workspace-store/events'\nimport { computed, ref, type ComputedRef } from 'vue'\nimport { useRouter } from 'vue-router'\n\nimport IconSelector from '@/components/IconSelector.vue'\n\nimport CommandActionForm from './CommandActionForm.vue'\nimport CommandActionInput from './CommandActionInput.vue'\n\nconst { workspaceStore, eventBus } = defineProps<{\n /** The workspace store for accessing existing documents */\n workspaceStore: WorkspaceStore\n /** Event bus for emitting document creation events */\n eventBus: WorkspaceEventBus\n}>()\n\nconst emit = defineEmits<{\n /** Emitted when the document is created successfully */\n (event: 'close'): void\n /** Emitted when user navigates back (e.g., backspace on empty input) */\n (event: 'back', keyboardEvent: KeyboardEvent): void\n}>()\n\nconst router = useRouter()\n\nconst documentName = ref('')\nconst documentNameTrimmed = computed(() => documentName.value.trim())\n\n/** Default icon for new documents (folder icon) */\nconst documentIcon = ref('interface-content-folder')\n\n/**\n * Validation message surfaced under the input.\n *\n * Resolves to `null` when the form is valid; otherwise to a human-readable\n * reason the user can act on. Empty input is the default state so we keep\n * the field free of error styling - `isDisabled` still blocks submission\n * there, matching the `CreateVersionModal` pattern.\n */\nconst errorMessage: ComputedRef<string | null> = computed(() => {\n if (!documentNameTrimmed.value) {\n return null\n }\n\n if (\n workspaceStore.workspace.documents[documentNameTrimmed.value] !== undefined\n ) {\n return `A document named \"${documentNameTrimmed.value}\" already exists. Try a different name.`\n }\n\n return null\n})\n\n/**\n * Submit is blocked while the input is empty or fails validation. We keep the\n * button disabled (rather than letting the user fire a no-op submit) because\n * the inline `errorMessage` already explains the fix out loud.\n */\nconst isDisabled = computed<boolean>(\n () => !documentNameTrimmed.value || errorMessage.value !== null,\n)\n\n/** Handle form submission to create a new document */\nconst handleSubmit = (): void => {\n if (isDisabled.value) {\n return\n }\n\n eventBus.emit('document:create:empty-document', {\n name: documentNameTrimmed.value,\n icon: documentIcon.value,\n callback: (success) => {\n if (success) {\n router.push({\n name: 'document.overview',\n params: {\n documentSlug: documentNameTrimmed.value,\n },\n })\n }\n },\n })\n\n emit('close')\n}\n\n/** Handle back navigation when user presses backspace on empty input */\nconst handleBack = (event: KeyboardEvent): void => {\n emit('back', event)\n}\n</script>\n\n<template>\n <CommandActionForm\n :disabled=\"isDisabled\"\n @submit=\"handleSubmit\">\n <CommandActionInput\n v-model=\"documentName\"\n label=\"Document Name\"\n placeholder=\"Document Name\"\n @delete=\"handleBack\" />\n\n <p\n v-if=\"errorMessage\"\n class=\"text-red px-2 pb-1 text-xs\"\n data-testid=\"command-palette-document-error\"\n role=\"alert\">\n {{ errorMessage }}\n </p>\n\n <!-- Icon selector for choosing document icon -->\n <template #options>\n <IconSelector\n v-model=\"documentIcon\"\n placement=\"bottom-start\">\n <ScalarButton\n class=\"aspect-square h-auto px-0\"\n variant=\"outlined\">\n <LibraryIcon\n class=\"text-c-2 size-4 stroke-[1.75]\"\n :src=\"documentIcon\" />\n </ScalarButton>\n </IconSelector>\n </template>\n\n <template #submit>Create Document</template>\n </CommandActionForm>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;EAuCA,MAAM,OAAO;EAOb,MAAM,SAAS,WAAU;EAEzB,MAAM,eAAe,IAAI,GAAE;EAC3B,MAAM,sBAAsB,eAAe,aAAa,MAAM,MAAM,CAAA;;EAGpE,MAAM,eAAe,IAAI,2BAA0B;;;;;;;;;EAUnD,MAAM,eAA2C,eAAe;AAC9D,OAAI,CAAC,oBAAoB,MACvB,QAAO;AAGT,OACE,QAAA,eAAe,UAAU,UAAU,oBAAoB,WAAW,KAAA,EAElE,QAAO,qBAAqB,oBAAoB,MAAM;AAGxD,UAAO;IACR;;;;;;EAOD,MAAM,aAAa,eACX,CAAC,oBAAoB,SAAS,aAAa,UAAU,KAC7D;;EAGA,MAAM,qBAA2B;AAC/B,OAAI,WAAW,MACb;AAGF,WAAA,SAAS,KAAK,kCAAkC;IAC9C,MAAM,oBAAoB;IAC1B,MAAM,aAAa;IACnB,WAAW,YAAY;AACrB,SAAI,QACF,QAAO,KAAK;MACV,MAAM;MACN,QAAQ,EACN,cAAc,oBAAoB,OACnC;MACF,CAAA;;IAGN,CAAA;AAED,QAAK,QAAO;;;EAId,MAAM,cAAc,UAA+B;AACjD,QAAK,QAAQ,MAAK;;;uBAKlB,YAiCoB,2BAAA;IAhCjB,UAAU,WAAA;IACV,UAAQ;;IAgBE,SAAO,cAWD,CAVf,YAUe,sBAAA;iBATJ,aAAA;+EAAY,QAAA;KACrB,WAAU;;4BAOK,CANf,YAMe,MAAA,aAAA,EAAA;MALb,OAAM;MACN,SAAQ;;6BAGgB,CAFxB,YAEwB,MAAA,YAAA,EAAA;OADtB,OAAM;OACL,KAAK,aAAA;;;;;;IAKH,QAAM,cAAgB,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CAAA,gBAAf,mBAAe,GAAA,CAAA,EAAA,CAAA;2BAzBR,CAJzB,YAIyB,4BAAA;iBAHd,aAAA;+EAAY,QAAA;KACrB,OAAM;KACN,aAAY;KACX,UAAQ;iCAGH,aAAA,SAAA,WAAA,EADR,mBAMI,KANJ,YAMI,gBADC,aAAA,MAAY,EAAA,EAAA,IAAA,mBAAA,IAAA,KAAA,CAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"generate-unique-slug.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/helpers/generate-unique-slug.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,GAAU,cAAc,MAAM,GAAG,SAAS,EAAE,kBAAkB,GAAG,CAAC,MAAM,CAAC,gCASvG,CAAA"}
1
+ {"version":3,"file":"generate-unique-slug.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/command-palette/helpers/generate-unique-slug.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,GAAU,cAAc,MAAM,GAAG,SAAS,EAAE,kBAAkB,GAAG,CAAC,MAAM,CAAC,gCASvG,CAAA"}
@@ -1,4 +1,4 @@
1
- import { slugify } from "../../../helpers/slugify.js";
1
+ import { slugify } from "@scalar/helpers/string/slugify";
2
2
  import { generateUniqueValue } from "@scalar/workspace-store/helpers/generate-unique-value";
3
3
  //#region src/v2/features/command-palette/helpers/generate-unique-slug.ts
4
4
  /**
@@ -24,7 +24,7 @@ var generateUniqueSlug = async (defaultValue, currentDocuments) => {
24
24
  defaultValue: defaultValue?.trim() || "default",
25
25
  validation: (value) => !currentDocuments.has(value),
26
26
  maxRetries: 100,
27
- transformation: slugify
27
+ transformation: (value) => slugify(value, { allowedSpecialChars: "." })
28
28
  });
29
29
  };
30
30
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"generate-unique-slug.js","names":[],"sources":["../../../../../src/v2/features/command-palette/helpers/generate-unique-slug.ts"],"sourcesContent":["import { generateUniqueValue } from '@scalar/workspace-store/helpers/generate-unique-value'\n\nimport { slugify } from '@/v2/helpers/slugify'\n\n/**\n * Generates a unique slug for an imported document based on its title.\n *\n * This ensures the imported document does not conflict with existing documents\n * by appending a number suffix if necessary (e.g., \"my-api\", \"my-api-1\", \"my-api-2\").\n *\n * The function will retry up to 100 times to find a unique slug. If all attempts fail,\n * it returns null, which should be handled as an import error.\n *\n * When the input is missing or contains only whitespace, it falls back to\n * `'default'` so the workspace store never ends up with a document keyed by an\n * empty string (for example, when a registry document has no `info.title`).\n *\n * @param defaultValue - The original document title to base the slug on\n * @param currentDocuments - Set of existing document slugs to check against\n *\n * @returns Promise resolving to a unique slug, or null if unable to generate one\n */\nexport const generateUniqueSlug = async (defaultValue: string | undefined, currentDocuments: Set<string>) => {\n const base = defaultValue?.trim() || 'default'\n\n return await generateUniqueValue({\n defaultValue: base,\n validation: (value) => !currentDocuments.has(value),\n maxRetries: 100,\n transformation: slugify,\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,qBAAqB,OAAO,cAAkC,qBAAkC;AAG3G,QAAO,MAAM,oBAAoB;EAC/B,cAHW,cAAc,MAAM,IAAI;EAInC,aAAa,UAAU,CAAC,iBAAiB,IAAI,MAAM;EACnD,YAAY;EACZ,gBAAgB;EACjB,CAAC"}
1
+ {"version":3,"file":"generate-unique-slug.js","names":[],"sources":["../../../../../src/v2/features/command-palette/helpers/generate-unique-slug.ts"],"sourcesContent":["import { slugify } from '@scalar/helpers/string/slugify'\nimport { generateUniqueValue } from '@scalar/workspace-store/helpers/generate-unique-value'\n\n/**\n * Generates a unique slug for an imported document based on its title.\n *\n * This ensures the imported document does not conflict with existing documents\n * by appending a number suffix if necessary (e.g., \"my-api\", \"my-api-1\", \"my-api-2\").\n *\n * The function will retry up to 100 times to find a unique slug. If all attempts fail,\n * it returns null, which should be handled as an import error.\n *\n * When the input is missing or contains only whitespace, it falls back to\n * `'default'` so the workspace store never ends up with a document keyed by an\n * empty string (for example, when a registry document has no `info.title`).\n *\n * @param defaultValue - The original document title to base the slug on\n * @param currentDocuments - Set of existing document slugs to check against\n *\n * @returns Promise resolving to a unique slug, or null if unable to generate one\n */\nexport const generateUniqueSlug = async (defaultValue: string | undefined, currentDocuments: Set<string>) => {\n const base = defaultValue?.trim() || 'default'\n\n return await generateUniqueValue({\n defaultValue: base,\n validation: (value) => !currentDocuments.has(value),\n maxRetries: 100,\n transformation: (value) => slugify(value, { allowedSpecialChars: '.' }),\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,qBAAqB,OAAO,cAAkC,qBAAkC;AAG3G,QAAO,MAAM,oBAAoB;EAC/B,cAHW,cAAc,MAAM,IAAI;EAInC,aAAa,UAAU,CAAC,iBAAiB,IAAI,MAAM;EACnD,YAAY;EACZ,iBAAiB,UAAU,QAAQ,OAAO,EAAE,qBAAqB,KAAK,CAAC;EACxE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-three-way-merge-editor.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/editor/hooks/use-three-way-merge-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAG3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,KAAK,gBAAgB,EAAsD,MAAM,KAAK,CAAA;AAY/F,KAAK,6BAA6B,GAAG;IACnC,uCAAuC;IACvC,YAAY,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACvD,yDAAyD;IACzD,gBAAgB,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3D,iCAAiC;IACjC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,CAAA;IAClE,iDAAiD;IACjD,cAAc,EAAE,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IACnE,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,qDAAqD;IACrD,KAAK,EAAE,WAAW,CAAA;IAClB,4CAA4C;IAC5C,MAAM,EAAE,WAAW,CAAA;IACnB,uDAAuD;IACvD,MAAM,EAAE,WAAW,CAAA;CACpB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG;IAC9E,IAAI,EAAE,CAAC,UAAU,EAAE,6BAA6B,KAAK,IAAI,CAAA;IACzD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAClC,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,sBAAsB,EAAE,MAAM,IAAI,CAAA;CACnC,CAydA"}
1
+ {"version":3,"file":"use-three-way-merge-editor.d.ts","sourceRoot":"","sources":["../../../../../src/v2/features/editor/hooks/use-three-way-merge-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAG3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,CAAA;AACtC,OAAO,EAAE,KAAK,gBAAgB,EAAsD,MAAM,KAAK,CAAA;AAY/F,KAAK,6BAA6B,GAAG;IACnC,uCAAuC;IACvC,YAAY,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACvD,yDAAyD;IACzD,gBAAgB,EAAE,gBAAgB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IAC3D,iCAAiC;IACjC,SAAS,EAAE,gBAAgB,CAAC,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,WAAW,CAAC,CAAC,CAAA;IAClE,iDAAiD;IACjD,cAAc,EAAE,CAAC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IACnE,8EAA8E;IAC9E,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC,CAAA;AAED,KAAK,6BAA6B,GAAG;IACnC,qDAAqD;IACrD,KAAK,EAAE,WAAW,CAAA;IAClB,4CAA4C;IAC5C,MAAM,EAAE,WAAW,CAAA;IACnB,uDAAuD;IACvD,MAAM,EAAE,WAAW,CAAA;CACpB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG;IAC9E,IAAI,EAAE,CAAC,UAAU,EAAE,6BAA6B,KAAK,IAAI,CAAA;IACzD,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,aAAa,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAClC,gBAAgB,EAAE,MAAM,IAAI,CAAA;IAC5B,sBAAsB,EAAE,MAAM,IAAI,CAAA;CACnC,CAoeA"}
@@ -4,8 +4,8 @@ import { ensureJsonPointerLinkSupport } from "../helpers/json/json-pointer-links
4
4
  import { createJsonModel } from "../helpers/json/create-json-model.js";
5
5
  import { computed, markRaw, ref, shallowRef, toValue, watch } from "vue";
6
6
  import { apply } from "@scalar/json-magic/diff";
7
- import * as monaco from "monaco-editor";
8
7
  import { deepClone } from "@scalar/workspace-store/helpers/deep-clone";
8
+ import * as monaco from "monaco-editor";
9
9
  //#region src/v2/features/editor/hooks/use-three-way-merge-editor.ts
10
10
  /**
11
11
  * Shared state and logic for a 3-way merge editor (base | local | remote → result).
@@ -69,8 +69,8 @@ function useThreeWayMergeEditor(options) {
69
69
  let modelsToDispose = [];
70
70
  const normalizeConflicts = computed(() => {
71
71
  return toValue(conflicts).map((conflict) => {
72
- const localChanges = conflict[0];
73
- const remoteChanges = conflict[1];
72
+ const remoteChanges = conflict[0];
73
+ const localChanges = conflict[1];
74
74
  let smallestPath = localChanges[0].path;
75
75
  for (const localChange of localChanges) if (localChange.path.length < smallestPath.length) smallestPath = localChange.path;
76
76
  for (const remoteChange of remoteChanges) if (remoteChange.path.length < smallestPath.length) smallestPath = remoteChange.path;
@@ -88,8 +88,8 @@ function useThreeWayMergeEditor(options) {
88
88
  };
89
89
  });
90
90
  });
91
- const documentWithLocalChanges = computed(() => apply(deepClone(toValue(resolvedDocument)), toValue(conflicts).flatMap((it) => it[0])));
92
- const documentWithRemoteChanges = computed(() => apply(deepClone(toValue(resolvedDocument)), toValue(conflicts).flatMap((it) => it[1])));
91
+ const documentWithRemoteChanges = computed(() => apply(deepClone(toValue(resolvedDocument)), toValue(conflicts).flatMap((it) => it[0])));
92
+ const documentWithLocalChanges = computed(() => apply(deepClone(toValue(resolvedDocument)), toValue(conflicts).flatMap((it) => it[1])));
93
93
  const conflictsLeft = computed(() => resolvedConflicts.value.filter((status) => status === "idle").length);
94
94
  watch(normalizeConflicts, (nextConflicts) => {
95
95
  resolvedConflicts.value = nextConflicts.map((_, index) => resolvedConflicts.value[index] ?? "idle");
@@ -1 +1 @@
1
- {"version":3,"file":"use-three-way-merge-editor.js","names":[],"sources":["../../../../../src/v2/features/editor/hooks/use-three-way-merge-editor.ts"],"sourcesContent":["import { apply, type merge } from '@scalar/json-magic/diff'\nimport { deepClone } from '@scalar/workspace-store/helpers/deep-clone'\nimport * as monaco from 'monaco-editor'\nimport type { ComputedRef } from 'vue'\nimport { type MaybeRefOrGetter, computed, markRaw, ref, shallowRef, toValue, watch } from 'vue'\n\nimport { rangeToWholeLine } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { ensureJsonPointerLinkSupport } from '@/v2/features/editor/helpers/json/json-pointer-links'\nimport { parseJsonPointerPath } from '@/v2/features/editor/helpers/json/json-pointer-path'\nimport type { EditorModel, Path } from '@/v2/features/editor/helpers/model'\n\ntype ConflictResolutionState = 'manual' | 'local' | 'remote' | 'ignore' | 'idle'\n\ntype ConflictRange = { index: number; path: string[]; range: monaco.Range }\n\ntype UseThreeWayMergeEditorOptions = {\n /** Base document (common ancestor). */\n baseDocument: MaybeRefOrGetter<Record<string, unknown>>\n /** Document with resolved conflicts (current result). */\n resolvedDocument: MaybeRefOrGetter<Record<string, unknown>>\n /** Merge conflicts from diff. */\n conflicts: MaybeRefOrGetter<ReturnType<typeof merge>['conflicts']>\n /** Called when user applies resolved changes. */\n onApplyChanges: (resolvedDocument: Record<string, unknown>) => void\n /** Optional: called with error message for conflicts left or parse errors. */\n onError?: (message: string) => void\n}\n\ntype ThreeWayMergeEditorContainers = {\n /** Container for the local (current) diff editor. */\n local: HTMLElement\n /** Container for the remote diff editor. */\n remote: HTMLElement\n /** Container for the result (editable) diff editor. */\n result: HTMLElement\n}\n\n/**\n * Shared state and logic for a 3-way merge editor (base | local | remote → result).\n * Create editors by calling init() with the three container elements (e.g. from refs in onMounted).\n * Call dispose() in onUnmounted.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * const localRef = ref<HTMLDivElement>()\n * const remoteRef = ref<HTMLDivElement>()\n * const resultRef = ref<HTMLDivElement>()\n *\n * const mergeEditor = useThreeWayMergeEditor({\n * baseDocument,\n * resolvedDocument,\n * conflicts,\n * onApplyChanges: (doc) => emit('applyChanges', { resolvedDocument: doc }),\n * onError: (msg) => toast(msg, 'error'),\n * })\n *\n * onMounted(() => {\n * const local = localRef.value\n * const remote = remoteRef.value\n * const result = resultRef.value\n * if (local && remote && result) {\n * mergeEditor.init({ local, remote, result })\n * }\n * })\n *\n * onUnmounted(() => {\n * mergeEditor.dispose()\n * })\n * </script>\n *\n * <template>\n * <div ref=\"localRef\" />\n * <div ref=\"remoteRef\" />\n * <div ref=\"resultRef\" />\n * <button @click=\"mergeEditor.goToNextConflict\">Next conflict</button>\n * <button :disabled=\"mergeEditor.conflictsLeft > 0\" @click=\"mergeEditor.applyResolvedConflicts\">\n * Apply ({{ mergeEditor.conflictsLeft }} left)\n * </button>\n * </template>\n * ```\n */\nexport function useThreeWayMergeEditor(options: UseThreeWayMergeEditorOptions): {\n init: (containers: ThreeWayMergeEditorContainers) => void\n dispose: () => void\n conflictsLeft: ComputedRef<number>\n goToNextConflict: () => void\n applyResolvedConflicts: () => void\n} {\n const { baseDocument, resolvedDocument, conflicts, onApplyChanges, onError } = options\n\n const resolvedConflicts = ref<ConflictResolutionState[]>([])\n const conflictRangesState = shallowRef<ConflictRange[]>([])\n const localConflictRangesState = shallowRef<ConflictRange[]>([])\n const remoteConflictRangesState = shallowRef<ConflictRange[]>([])\n const lastNavigatedConflictIndex = ref<number>(-1)\n\n const localChangesEditor = shallowRef<monaco.editor.IStandaloneDiffEditor>()\n const remoteChangesEditor = shallowRef<monaco.editor.IStandaloneDiffEditor>()\n const resultEditor = shallowRef<monaco.editor.IStandaloneDiffEditor>()\n\n let resultCodeLensProviderDisposable: monaco.IDisposable | undefined\n let jsonPointerLinkSupportDispose: (() => void) | undefined\n const codeLensCommandDisposables: monaco.IDisposable[] = []\n /** Models created in init(); disposed in dispose() to avoid leaks when the sync conflict modal closes. */\n let modelsToDispose: monaco.editor.ITextModel[] = []\n\n const normalizeConflicts = computed(() => {\n const conflictList = toValue(conflicts)\n return conflictList.map((conflict) => {\n const localChanges = conflict[0]\n const remoteChanges = conflict[1]\n let smallestPath = localChanges[0]!.path\n for (const localChange of localChanges) {\n if (localChange.path.length < smallestPath.length) {\n smallestPath = localChange.path\n }\n }\n for (const remoteChange of remoteChanges) {\n if (remoteChange.path.length < smallestPath.length) {\n smallestPath = remoteChange.path\n }\n }\n return {\n local: {\n path: smallestPath,\n changes: localChanges,\n type: localChanges[0]!.type,\n },\n remote: {\n path: smallestPath,\n changes: remoteChanges,\n type: remoteChanges[0]!.type,\n },\n }\n })\n })\n\n const documentWithLocalChanges = computed(() =>\n apply(\n deepClone(toValue(resolvedDocument)),\n toValue(conflicts).flatMap((it) => it[0]),\n ),\n )\n\n const documentWithRemoteChanges = computed(() =>\n apply(\n deepClone(toValue(resolvedDocument)),\n toValue(conflicts).flatMap((it) => it[1]),\n ),\n )\n\n const conflictsLeft = computed(() => resolvedConflicts.value.filter((status) => status === 'idle').length)\n\n watch(\n normalizeConflicts,\n (nextConflicts) => {\n resolvedConflicts.value = nextConflicts.map((_, index) => resolvedConflicts.value[index] ?? 'idle')\n },\n { immediate: true },\n )\n\n const focusEditorRange = (\n diffEditor: monaco.editor.IStandaloneDiffEditor | undefined,\n range: monaco.Range | undefined,\n ): void => {\n if (!diffEditor || !range) {\n return\n }\n const modifiedEditor = diffEditor.getModifiedEditor()\n const safeRange = new monaco.Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)\n modifiedEditor.setSelection(safeRange)\n modifiedEditor.revealRangeNearTop(safeRange)\n }\n\n const goToNextConflict = (): void => {\n const unresolvedConflictIndexes = normalizeConflicts.value\n .map((_, index) => index)\n .filter((index) => resolvedConflicts.value[index] === 'idle')\n if (!unresolvedConflictIndexes.length) {\n return\n }\n const nextConflictIndex =\n unresolvedConflictIndexes.find((index) => index > lastNavigatedConflictIndex.value) ??\n unresolvedConflictIndexes[0]\n if (nextConflictIndex === undefined) {\n return\n }\n const nextConflict = normalizeConflicts.value[nextConflictIndex]\n if (!nextConflict) {\n return\n }\n const localRange = localConflictRangesState.value.find((cr) => cr.index === nextConflictIndex)?.range\n const remoteRange = remoteConflictRangesState.value.find((cr) => cr.index === nextConflictIndex)?.range\n const resultRange = conflictRangesState.value.find((cr) => cr.index === nextConflictIndex)?.range\n focusEditorRange(localChangesEditor.value, localRange)\n focusEditorRange(remoteChangesEditor.value, remoteRange)\n focusEditorRange(resultEditor.value, resultRange)\n resultEditor.value?.getModifiedEditor().focus()\n lastNavigatedConflictIndex.value = nextConflictIndex\n }\n\n const applyResolvedConflicts = (): void => {\n if (conflictsLeft.value > 0) {\n onError?.('You have conflicts left')\n return\n }\n const modifiedResultModel = resultEditor.value?.getModel()?.modified\n if (!modifiedResultModel) {\n return\n }\n let nextResolvedDocument: Record<string, unknown> | null = null\n try {\n nextResolvedDocument = JSON.parse(modifiedResultModel.getValue()) as Record<string, unknown>\n } catch {\n onError?.('You have formatting errors')\n return\n }\n onApplyChanges(nextResolvedDocument)\n }\n\n const init = (containers: ThreeWayMergeEditorContainers): void => {\n const base = toValue(baseDocument)\n const resolved = toValue(resolvedDocument)\n const localDoc = documentWithLocalChanges.value\n const remoteDoc = documentWithRemoteChanges.value\n\n const originalModelLocal = monaco.editor.createModel(JSON.stringify(resolved, null, 2), 'json')\n const modifiedModelLocal = createJsonModel(JSON.stringify(localDoc, null, 2))\n\n const originalModelRemote = monaco.editor.createModel(JSON.stringify(resolved, null, 2), 'json')\n const modifiedModelRemote = createJsonModel(JSON.stringify(remoteDoc, null, 2))\n\n const originalResultModel = monaco.editor.createModel(JSON.stringify(base, null, 2), 'json')\n const modifiedResultModel = createJsonModel(JSON.stringify(resolved, null, 2))\n\n modelsToDispose = [\n originalModelLocal,\n modifiedModelLocal.model,\n originalModelRemote,\n modifiedModelRemote.model,\n originalResultModel,\n modifiedResultModel.model,\n ]\n\n const localDiffEditor = monaco.editor.createDiffEditor(containers.local, {\n originalEditable: false,\n readOnly: true,\n automaticLayout: true,\n renderSideBySide: false,\n })\n localDiffEditor.setModel({\n original: originalModelLocal,\n modified: modifiedModelLocal.model,\n })\n localChangesEditor.value = localDiffEditor\n\n const remoteDiffEditor = monaco.editor.createDiffEditor(containers.remote, {\n originalEditable: false,\n readOnly: true,\n automaticLayout: true,\n renderSideBySide: false,\n })\n remoteDiffEditor.setModel({\n original: originalModelRemote,\n modified: modifiedModelRemote.model,\n })\n remoteChangesEditor.value = remoteDiffEditor\n\n const resultDiffEditor = monaco.editor.createDiffEditor(containers.result, {\n originalEditable: false,\n readOnly: false,\n renderSideBySide: false,\n automaticLayout: true,\n })\n resultDiffEditor.setModel({\n original: originalResultModel,\n modified: modifiedResultModel.model,\n })\n const resultModifiedEditor = resultDiffEditor.getModifiedEditor()\n resultModifiedEditor.updateOptions({ codeLens: true })\n\n const focusResultPath = async (path: Path): Promise<void> => {\n const range = await modifiedResultModel.getRangeFromPath(path)\n if (!range) {\n return\n }\n const safeRange = new monaco.Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)\n resultModifiedEditor.setSelection(safeRange)\n resultModifiedEditor.revealRangeNearTop(safeRange)\n }\n\n const navigateJsonPointer = async (pointer: string): Promise<void> => {\n const path = parseJsonPointerPath(pointer)\n if (!path) {\n return\n }\n await focusResultPath(path)\n }\n\n const linkSupport = ensureJsonPointerLinkSupport(navigateJsonPointer)\n jsonPointerLinkSupportDispose = linkSupport.dispose\n\n let resultHighlightDecorations: string[] = []\n let suppressedChangeEvents = 0\n\n const applyCurrentCodeLensCommandId = 'scalar.conflict.applyCurrent'\n const applyRemoteCodeLensCommandId = 'scalar.conflict.applyRemote'\n const ignoreCodeLensCommandId = 'scalar.conflict.ignore'\n const noOpCodeLensCommandId = resultModifiedEditor.addCommand(0, () => undefined) ?? 'scalar.conflict.status.noop'\n\n const getBoxDecorationsForRange = (range: monaco.Range): monaco.editor.IModelDeltaDecoration[] => {\n const startLine = range.startLineNumber\n const endLine = range.endLineNumber\n if (startLine === endLine) {\n return [\n {\n range,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-single',\n },\n },\n ]\n }\n const topRange = new monaco.Range(startLine, 1, startLine, range.endColumn)\n const middleRange = new monaco.Range(startLine + 1, 1, Math.max(startLine + 1, endLine - 1), 1)\n const bottomRange = new monaco.Range(endLine, 1, endLine, range.endColumn)\n const decorations: monaco.editor.IModelDeltaDecoration[] = [\n {\n range: topRange,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-top',\n },\n },\n {\n range: bottomRange,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-bottom',\n },\n },\n ]\n if (endLine - startLine > 1) {\n decorations.push({\n range: middleRange,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-middle',\n },\n })\n }\n return decorations\n }\n\n const refreshCodeLensProvider = (): void => {\n resultCodeLensProviderDisposable?.dispose()\n resultCodeLensProviderDisposable = monaco.languages.registerCodeLensProvider('json', {\n provideCodeLenses: (model) => {\n if (model.uri.toString() !== modifiedResultModel.model.uri.toString()) {\n return { lenses: [], dispose: () => undefined }\n }\n return {\n lenses: conflictRangesState.value.flatMap((conflictRange) => {\n const lensRange = new monaco.Range(\n conflictRange.range.startLineNumber,\n 1,\n conflictRange.range.startLineNumber,\n 1,\n )\n return [\n {\n range: lensRange,\n command: {\n id: noOpCodeLensCommandId,\n title: `Status: ${resolvedConflicts.value[conflictRange.index] ?? 'idle'}`,\n },\n },\n {\n range: lensRange,\n command: {\n id: applyCurrentCodeLensCommandId,\n title: 'Accept Current',\n arguments: [conflictRange.index],\n },\n },\n {\n range: lensRange,\n command: {\n id: applyRemoteCodeLensCommandId,\n title: 'Accept Remote',\n arguments: [conflictRange.index],\n },\n },\n {\n range: lensRange,\n command: {\n id: ignoreCodeLensCommandId,\n title: 'Ignore',\n arguments: [conflictRange.index],\n },\n },\n ]\n }),\n dispose: () => undefined,\n }\n },\n })\n }\n\n const getConflictRange = async (\n editorModel: EditorModel,\n index: number,\n path: string[],\n ): Promise<ConflictRange> => {\n const { model } = editorModel\n const fallbackRange = new monaco.Range(1, 1, 1, model.getLineMaxColumn(1))\n let range: monaco.Range | null = null\n for (let depth = path.length; depth >= 0; depth -= 1) {\n const candidatePath = path.slice(0, depth)\n const nodeRange = await editorModel.getRangeFromPath(candidatePath)\n if (!nodeRange) {\n continue\n }\n range = rangeToWholeLine(model, nodeRange)\n break\n }\n return {\n index,\n path,\n range: markRaw(range ?? fallbackRange),\n }\n }\n\n const refreshConflictRangesAndDecorations = async (): Promise<void> => {\n const [resultRanges, localRanges, remoteRanges] = await Promise.all([\n Promise.all(\n normalizeConflicts.value.map((conflict, index) =>\n getConflictRange(modifiedResultModel, index, conflict.local.path),\n ),\n ),\n Promise.all(\n normalizeConflicts.value.map((conflict, index) =>\n getConflictRange(modifiedModelLocal, index, conflict.local.path),\n ),\n ),\n Promise.all(\n normalizeConflicts.value.map((conflict, index) =>\n getConflictRange(modifiedModelRemote, index, conflict.local.path),\n ),\n ),\n ])\n conflictRangesState.value = resultRanges.filter((r): r is ConflictRange => Boolean(r))\n localConflictRangesState.value = localRanges.filter((r): r is ConflictRange => Boolean(r))\n remoteConflictRangesState.value = remoteRanges.filter((r): r is ConflictRange => Boolean(r))\n resultHighlightDecorations = resultModifiedEditor.deltaDecorations(\n resultHighlightDecorations,\n conflictRangesState.value.flatMap((cr) => getBoxDecorationsForRange(cr.range)),\n )\n refreshCodeLensProvider()\n }\n\n const setConflictResolution = (index: number, resolution: ConflictResolutionState): void => {\n resolvedConflicts.value[index] = resolution\n refreshCodeLensProvider()\n }\n\n const applyConflictChoice = async (index: number, source: 'local' | 'remote'): Promise<void> => {\n const conflict = normalizeConflicts.value[index]\n if (!conflict) {\n return\n }\n let currentDocument: Record<string, unknown> | null = null\n try {\n currentDocument = JSON.parse(modifiedResultModel.model.getValue()) as Record<string, unknown>\n } catch {\n return\n }\n const changes = source === 'local' ? conflict.local.changes : conflict.remote.changes\n const nextDocument = apply(deepClone(currentDocument), changes)\n suppressedChangeEvents += 1\n modifiedResultModel.model.setValue(JSON.stringify(nextDocument, null, 2))\n setConflictResolution(index, source)\n await refreshConflictRangesAndDecorations()\n }\n\n codeLensCommandDisposables.push(\n monaco.editor.registerCommand(applyCurrentCodeLensCommandId, (_accessor, index: number) => {\n void applyConflictChoice(index, 'local')\n }),\n )\n codeLensCommandDisposables.push(\n monaco.editor.registerCommand(applyRemoteCodeLensCommandId, (_accessor, index: number) => {\n void applyConflictChoice(index, 'remote')\n }),\n )\n codeLensCommandDisposables.push(\n monaco.editor.registerCommand(ignoreCodeLensCommandId, (_accessor, index: number) => {\n setConflictResolution(index, 'ignore')\n }),\n )\n\n void refreshConflictRangesAndDecorations()\n\n resultModifiedEditor.onDidChangeModelContent((event) => {\n if (suppressedChangeEvents > 0) {\n suppressedChangeEvents -= 1\n return\n }\n let hasStatusUpdates = false\n for (const change of event.changes) {\n const matchingConflicts = conflictRangesState.value.filter((cr) =>\n monaco.Range.areIntersectingOrTouching(change.range, cr.range),\n )\n if (!matchingConflicts.length) {\n continue\n }\n matchingConflicts.forEach((conflict) => {\n if (resolvedConflicts.value[conflict.index] !== 'manual') {\n resolvedConflicts.value[conflict.index] = 'manual'\n hasStatusUpdates = true\n }\n })\n }\n if (hasStatusUpdates) {\n refreshCodeLensProvider()\n }\n })\n\n resultEditor.value = resultDiffEditor\n }\n\n const dispose = (): void => {\n localChangesEditor.value?.dispose()\n localChangesEditor.value = undefined\n remoteChangesEditor.value?.dispose()\n remoteChangesEditor.value = undefined\n resultEditor.value?.dispose()\n resultEditor.value = undefined\n\n for (const model of modelsToDispose) {\n model.dispose()\n }\n modelsToDispose = []\n\n resultCodeLensProviderDisposable?.dispose()\n resultCodeLensProviderDisposable = undefined\n jsonPointerLinkSupportDispose?.()\n jsonPointerLinkSupportDispose = undefined\n codeLensCommandDisposables.forEach((d) => d.dispose())\n codeLensCommandDisposables.length = 0\n }\n\n return {\n init,\n dispose,\n conflictsLeft,\n goToNextConflict,\n applyResolvedConflicts,\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-three-way-merge-editor.js","names":[],"sources":["../../../../../src/v2/features/editor/hooks/use-three-way-merge-editor.ts"],"sourcesContent":["import { apply, type merge } from '@scalar/json-magic/diff'\nimport { deepClone } from '@scalar/workspace-store/helpers/deep-clone'\nimport * as monaco from 'monaco-editor'\nimport type { ComputedRef } from 'vue'\nimport { type MaybeRefOrGetter, computed, markRaw, ref, shallowRef, toValue, watch } from 'vue'\n\nimport { rangeToWholeLine } from '@/v2/features/editor'\nimport { createJsonModel } from '@/v2/features/editor/helpers/json/create-json-model'\nimport { ensureJsonPointerLinkSupport } from '@/v2/features/editor/helpers/json/json-pointer-links'\nimport { parseJsonPointerPath } from '@/v2/features/editor/helpers/json/json-pointer-path'\nimport type { EditorModel, Path } from '@/v2/features/editor/helpers/model'\n\ntype ConflictResolutionState = 'manual' | 'local' | 'remote' | 'ignore' | 'idle'\n\ntype ConflictRange = { index: number; path: string[]; range: monaco.Range }\n\ntype UseThreeWayMergeEditorOptions = {\n /** Base document (common ancestor). */\n baseDocument: MaybeRefOrGetter<Record<string, unknown>>\n /** Document with resolved conflicts (current result). */\n resolvedDocument: MaybeRefOrGetter<Record<string, unknown>>\n /** Merge conflicts from diff. */\n conflicts: MaybeRefOrGetter<ReturnType<typeof merge>['conflicts']>\n /** Called when user applies resolved changes. */\n onApplyChanges: (resolvedDocument: Record<string, unknown>) => void\n /** Optional: called with error message for conflicts left or parse errors. */\n onError?: (message: string) => void\n}\n\ntype ThreeWayMergeEditorContainers = {\n /** Container for the local (current) diff editor. */\n local: HTMLElement\n /** Container for the remote diff editor. */\n remote: HTMLElement\n /** Container for the result (editable) diff editor. */\n result: HTMLElement\n}\n\n/**\n * Shared state and logic for a 3-way merge editor (base | local | remote → result).\n * Create editors by calling init() with the three container elements (e.g. from refs in onMounted).\n * Call dispose() in onUnmounted.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * const localRef = ref<HTMLDivElement>()\n * const remoteRef = ref<HTMLDivElement>()\n * const resultRef = ref<HTMLDivElement>()\n *\n * const mergeEditor = useThreeWayMergeEditor({\n * baseDocument,\n * resolvedDocument,\n * conflicts,\n * onApplyChanges: (doc) => emit('applyChanges', { resolvedDocument: doc }),\n * onError: (msg) => toast(msg, 'error'),\n * })\n *\n * onMounted(() => {\n * const local = localRef.value\n * const remote = remoteRef.value\n * const result = resultRef.value\n * if (local && remote && result) {\n * mergeEditor.init({ local, remote, result })\n * }\n * })\n *\n * onUnmounted(() => {\n * mergeEditor.dispose()\n * })\n * </script>\n *\n * <template>\n * <div ref=\"localRef\" />\n * <div ref=\"remoteRef\" />\n * <div ref=\"resultRef\" />\n * <button @click=\"mergeEditor.goToNextConflict\">Next conflict</button>\n * <button :disabled=\"mergeEditor.conflictsLeft > 0\" @click=\"mergeEditor.applyResolvedConflicts\">\n * Apply ({{ mergeEditor.conflictsLeft }} left)\n * </button>\n * </template>\n * ```\n */\nexport function useThreeWayMergeEditor(options: UseThreeWayMergeEditorOptions): {\n init: (containers: ThreeWayMergeEditorContainers) => void\n dispose: () => void\n conflictsLeft: ComputedRef<number>\n goToNextConflict: () => void\n applyResolvedConflicts: () => void\n} {\n const { baseDocument, resolvedDocument, conflicts, onApplyChanges, onError } = options\n\n const resolvedConflicts = ref<ConflictResolutionState[]>([])\n const conflictRangesState = shallowRef<ConflictRange[]>([])\n const localConflictRangesState = shallowRef<ConflictRange[]>([])\n const remoteConflictRangesState = shallowRef<ConflictRange[]>([])\n const lastNavigatedConflictIndex = ref<number>(-1)\n\n const localChangesEditor = shallowRef<monaco.editor.IStandaloneDiffEditor>()\n const remoteChangesEditor = shallowRef<monaco.editor.IStandaloneDiffEditor>()\n const resultEditor = shallowRef<monaco.editor.IStandaloneDiffEditor>()\n\n let resultCodeLensProviderDisposable: monaco.IDisposable | undefined\n let jsonPointerLinkSupportDispose: (() => void) | undefined\n const codeLensCommandDisposables: monaco.IDisposable[] = []\n /** Models created in init(); disposed in dispose() to avoid leaks when the sync conflict modal closes. */\n let modelsToDispose: monaco.editor.ITextModel[] = []\n\n const normalizeConflicts = computed(() => {\n const conflictList = toValue(conflicts)\n return conflictList.map((conflict) => {\n // Conflict tuples follow the workspace-store convention emitted by\n // `rebaseDocument`: `[changelogIncoming, changelogLocal]` -\n // i.e. index 0 is the upstream/remote change and index 1 is the\n // local edit. Aligning here means every consumer of this hook\n // (the in-page Sync flow in `DocumentCollection.vue` and the\n // header Pull flow in `App.vue`) can pass `result.conflicts`\n // straight through without transposing.\n const remoteChanges = conflict[0]\n const localChanges = conflict[1]\n let smallestPath = localChanges[0]!.path\n for (const localChange of localChanges) {\n if (localChange.path.length < smallestPath.length) {\n smallestPath = localChange.path\n }\n }\n for (const remoteChange of remoteChanges) {\n if (remoteChange.path.length < smallestPath.length) {\n smallestPath = remoteChange.path\n }\n }\n return {\n local: {\n path: smallestPath,\n changes: localChanges,\n type: localChanges[0]!.type,\n },\n remote: {\n path: smallestPath,\n changes: remoteChanges,\n type: remoteChanges[0]!.type,\n },\n }\n })\n })\n\n // Pane previews follow the same `[remote, local]` tuple convention as\n // `normalizeConflicts` above: index 0 is the upstream change set, so\n // `documentWithRemoteChanges` reads from `it[0]` and the local /\n // \"Current\" pane reads from `it[1]`.\n const documentWithRemoteChanges = computed(() =>\n apply(\n deepClone(toValue(resolvedDocument)),\n toValue(conflicts).flatMap((it) => it[0]),\n ),\n )\n\n const documentWithLocalChanges = computed(() =>\n apply(\n deepClone(toValue(resolvedDocument)),\n toValue(conflicts).flatMap((it) => it[1]),\n ),\n )\n\n const conflictsLeft = computed(() => resolvedConflicts.value.filter((status) => status === 'idle').length)\n\n watch(\n normalizeConflicts,\n (nextConflicts) => {\n resolvedConflicts.value = nextConflicts.map((_, index) => resolvedConflicts.value[index] ?? 'idle')\n },\n { immediate: true },\n )\n\n const focusEditorRange = (\n diffEditor: monaco.editor.IStandaloneDiffEditor | undefined,\n range: monaco.Range | undefined,\n ): void => {\n if (!diffEditor || !range) {\n return\n }\n const modifiedEditor = diffEditor.getModifiedEditor()\n const safeRange = new monaco.Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)\n modifiedEditor.setSelection(safeRange)\n modifiedEditor.revealRangeNearTop(safeRange)\n }\n\n const goToNextConflict = (): void => {\n const unresolvedConflictIndexes = normalizeConflicts.value\n .map((_, index) => index)\n .filter((index) => resolvedConflicts.value[index] === 'idle')\n if (!unresolvedConflictIndexes.length) {\n return\n }\n const nextConflictIndex =\n unresolvedConflictIndexes.find((index) => index > lastNavigatedConflictIndex.value) ??\n unresolvedConflictIndexes[0]\n if (nextConflictIndex === undefined) {\n return\n }\n const nextConflict = normalizeConflicts.value[nextConflictIndex]\n if (!nextConflict) {\n return\n }\n const localRange = localConflictRangesState.value.find((cr) => cr.index === nextConflictIndex)?.range\n const remoteRange = remoteConflictRangesState.value.find((cr) => cr.index === nextConflictIndex)?.range\n const resultRange = conflictRangesState.value.find((cr) => cr.index === nextConflictIndex)?.range\n focusEditorRange(localChangesEditor.value, localRange)\n focusEditorRange(remoteChangesEditor.value, remoteRange)\n focusEditorRange(resultEditor.value, resultRange)\n resultEditor.value?.getModifiedEditor().focus()\n lastNavigatedConflictIndex.value = nextConflictIndex\n }\n\n const applyResolvedConflicts = (): void => {\n if (conflictsLeft.value > 0) {\n onError?.('You have conflicts left')\n return\n }\n const modifiedResultModel = resultEditor.value?.getModel()?.modified\n if (!modifiedResultModel) {\n return\n }\n let nextResolvedDocument: Record<string, unknown> | null = null\n try {\n nextResolvedDocument = JSON.parse(modifiedResultModel.getValue()) as Record<string, unknown>\n } catch {\n onError?.('You have formatting errors')\n return\n }\n onApplyChanges(nextResolvedDocument)\n }\n\n const init = (containers: ThreeWayMergeEditorContainers): void => {\n const base = toValue(baseDocument)\n const resolved = toValue(resolvedDocument)\n const localDoc = documentWithLocalChanges.value\n const remoteDoc = documentWithRemoteChanges.value\n\n const originalModelLocal = monaco.editor.createModel(JSON.stringify(resolved, null, 2), 'json')\n const modifiedModelLocal = createJsonModel(JSON.stringify(localDoc, null, 2))\n\n const originalModelRemote = monaco.editor.createModel(JSON.stringify(resolved, null, 2), 'json')\n const modifiedModelRemote = createJsonModel(JSON.stringify(remoteDoc, null, 2))\n\n const originalResultModel = monaco.editor.createModel(JSON.stringify(base, null, 2), 'json')\n const modifiedResultModel = createJsonModel(JSON.stringify(resolved, null, 2))\n\n modelsToDispose = [\n originalModelLocal,\n modifiedModelLocal.model,\n originalModelRemote,\n modifiedModelRemote.model,\n originalResultModel,\n modifiedResultModel.model,\n ]\n\n const localDiffEditor = monaco.editor.createDiffEditor(containers.local, {\n originalEditable: false,\n readOnly: true,\n automaticLayout: true,\n renderSideBySide: false,\n })\n localDiffEditor.setModel({\n original: originalModelLocal,\n modified: modifiedModelLocal.model,\n })\n localChangesEditor.value = localDiffEditor\n\n const remoteDiffEditor = monaco.editor.createDiffEditor(containers.remote, {\n originalEditable: false,\n readOnly: true,\n automaticLayout: true,\n renderSideBySide: false,\n })\n remoteDiffEditor.setModel({\n original: originalModelRemote,\n modified: modifiedModelRemote.model,\n })\n remoteChangesEditor.value = remoteDiffEditor\n\n const resultDiffEditor = monaco.editor.createDiffEditor(containers.result, {\n originalEditable: false,\n readOnly: false,\n renderSideBySide: false,\n automaticLayout: true,\n })\n resultDiffEditor.setModel({\n original: originalResultModel,\n modified: modifiedResultModel.model,\n })\n const resultModifiedEditor = resultDiffEditor.getModifiedEditor()\n resultModifiedEditor.updateOptions({ codeLens: true })\n\n const focusResultPath = async (path: Path): Promise<void> => {\n const range = await modifiedResultModel.getRangeFromPath(path)\n if (!range) {\n return\n }\n const safeRange = new monaco.Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)\n resultModifiedEditor.setSelection(safeRange)\n resultModifiedEditor.revealRangeNearTop(safeRange)\n }\n\n const navigateJsonPointer = async (pointer: string): Promise<void> => {\n const path = parseJsonPointerPath(pointer)\n if (!path) {\n return\n }\n await focusResultPath(path)\n }\n\n const linkSupport = ensureJsonPointerLinkSupport(navigateJsonPointer)\n jsonPointerLinkSupportDispose = linkSupport.dispose\n\n let resultHighlightDecorations: string[] = []\n let suppressedChangeEvents = 0\n\n const applyCurrentCodeLensCommandId = 'scalar.conflict.applyCurrent'\n const applyRemoteCodeLensCommandId = 'scalar.conflict.applyRemote'\n const ignoreCodeLensCommandId = 'scalar.conflict.ignore'\n const noOpCodeLensCommandId = resultModifiedEditor.addCommand(0, () => undefined) ?? 'scalar.conflict.status.noop'\n\n const getBoxDecorationsForRange = (range: monaco.Range): monaco.editor.IModelDeltaDecoration[] => {\n const startLine = range.startLineNumber\n const endLine = range.endLineNumber\n if (startLine === endLine) {\n return [\n {\n range,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-single',\n },\n },\n ]\n }\n const topRange = new monaco.Range(startLine, 1, startLine, range.endColumn)\n const middleRange = new monaco.Range(startLine + 1, 1, Math.max(startLine + 1, endLine - 1), 1)\n const bottomRange = new monaco.Range(endLine, 1, endLine, range.endColumn)\n const decorations: monaco.editor.IModelDeltaDecoration[] = [\n {\n range: topRange,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-top',\n },\n },\n {\n range: bottomRange,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-bottom',\n },\n },\n ]\n if (endLine - startLine > 1) {\n decorations.push({\n range: middleRange,\n options: {\n isWholeLine: true,\n className: 'json-focus-highlight-box-middle',\n },\n })\n }\n return decorations\n }\n\n const refreshCodeLensProvider = (): void => {\n resultCodeLensProviderDisposable?.dispose()\n resultCodeLensProviderDisposable = monaco.languages.registerCodeLensProvider('json', {\n provideCodeLenses: (model) => {\n if (model.uri.toString() !== modifiedResultModel.model.uri.toString()) {\n return { lenses: [], dispose: () => undefined }\n }\n return {\n lenses: conflictRangesState.value.flatMap((conflictRange) => {\n const lensRange = new monaco.Range(\n conflictRange.range.startLineNumber,\n 1,\n conflictRange.range.startLineNumber,\n 1,\n )\n return [\n {\n range: lensRange,\n command: {\n id: noOpCodeLensCommandId,\n title: `Status: ${resolvedConflicts.value[conflictRange.index] ?? 'idle'}`,\n },\n },\n {\n range: lensRange,\n command: {\n id: applyCurrentCodeLensCommandId,\n title: 'Accept Current',\n arguments: [conflictRange.index],\n },\n },\n {\n range: lensRange,\n command: {\n id: applyRemoteCodeLensCommandId,\n title: 'Accept Remote',\n arguments: [conflictRange.index],\n },\n },\n {\n range: lensRange,\n command: {\n id: ignoreCodeLensCommandId,\n title: 'Ignore',\n arguments: [conflictRange.index],\n },\n },\n ]\n }),\n dispose: () => undefined,\n }\n },\n })\n }\n\n const getConflictRange = async (\n editorModel: EditorModel,\n index: number,\n path: string[],\n ): Promise<ConflictRange> => {\n const { model } = editorModel\n const fallbackRange = new monaco.Range(1, 1, 1, model.getLineMaxColumn(1))\n let range: monaco.Range | null = null\n for (let depth = path.length; depth >= 0; depth -= 1) {\n const candidatePath = path.slice(0, depth)\n const nodeRange = await editorModel.getRangeFromPath(candidatePath)\n if (!nodeRange) {\n continue\n }\n range = rangeToWholeLine(model, nodeRange)\n break\n }\n return {\n index,\n path,\n range: markRaw(range ?? fallbackRange),\n }\n }\n\n const refreshConflictRangesAndDecorations = async (): Promise<void> => {\n const [resultRanges, localRanges, remoteRanges] = await Promise.all([\n Promise.all(\n normalizeConflicts.value.map((conflict, index) =>\n getConflictRange(modifiedResultModel, index, conflict.local.path),\n ),\n ),\n Promise.all(\n normalizeConflicts.value.map((conflict, index) =>\n getConflictRange(modifiedModelLocal, index, conflict.local.path),\n ),\n ),\n Promise.all(\n normalizeConflicts.value.map((conflict, index) =>\n getConflictRange(modifiedModelRemote, index, conflict.local.path),\n ),\n ),\n ])\n conflictRangesState.value = resultRanges.filter((r): r is ConflictRange => Boolean(r))\n localConflictRangesState.value = localRanges.filter((r): r is ConflictRange => Boolean(r))\n remoteConflictRangesState.value = remoteRanges.filter((r): r is ConflictRange => Boolean(r))\n resultHighlightDecorations = resultModifiedEditor.deltaDecorations(\n resultHighlightDecorations,\n conflictRangesState.value.flatMap((cr) => getBoxDecorationsForRange(cr.range)),\n )\n refreshCodeLensProvider()\n }\n\n const setConflictResolution = (index: number, resolution: ConflictResolutionState): void => {\n resolvedConflicts.value[index] = resolution\n refreshCodeLensProvider()\n }\n\n const applyConflictChoice = async (index: number, source: 'local' | 'remote'): Promise<void> => {\n const conflict = normalizeConflicts.value[index]\n if (!conflict) {\n return\n }\n let currentDocument: Record<string, unknown> | null = null\n try {\n currentDocument = JSON.parse(modifiedResultModel.model.getValue()) as Record<string, unknown>\n } catch {\n return\n }\n const changes = source === 'local' ? conflict.local.changes : conflict.remote.changes\n const nextDocument = apply(deepClone(currentDocument), changes)\n suppressedChangeEvents += 1\n modifiedResultModel.model.setValue(JSON.stringify(nextDocument, null, 2))\n setConflictResolution(index, source)\n await refreshConflictRangesAndDecorations()\n }\n\n codeLensCommandDisposables.push(\n monaco.editor.registerCommand(applyCurrentCodeLensCommandId, (_accessor, index: number) => {\n void applyConflictChoice(index, 'local')\n }),\n )\n codeLensCommandDisposables.push(\n monaco.editor.registerCommand(applyRemoteCodeLensCommandId, (_accessor, index: number) => {\n void applyConflictChoice(index, 'remote')\n }),\n )\n codeLensCommandDisposables.push(\n monaco.editor.registerCommand(ignoreCodeLensCommandId, (_accessor, index: number) => {\n setConflictResolution(index, 'ignore')\n }),\n )\n\n void refreshConflictRangesAndDecorations()\n\n resultModifiedEditor.onDidChangeModelContent((event) => {\n if (suppressedChangeEvents > 0) {\n suppressedChangeEvents -= 1\n return\n }\n let hasStatusUpdates = false\n for (const change of event.changes) {\n const matchingConflicts = conflictRangesState.value.filter((cr) =>\n monaco.Range.areIntersectingOrTouching(change.range, cr.range),\n )\n if (!matchingConflicts.length) {\n continue\n }\n matchingConflicts.forEach((conflict) => {\n if (resolvedConflicts.value[conflict.index] !== 'manual') {\n resolvedConflicts.value[conflict.index] = 'manual'\n hasStatusUpdates = true\n }\n })\n }\n if (hasStatusUpdates) {\n refreshCodeLensProvider()\n }\n })\n\n resultEditor.value = resultDiffEditor\n }\n\n const dispose = (): void => {\n localChangesEditor.value?.dispose()\n localChangesEditor.value = undefined\n remoteChangesEditor.value?.dispose()\n remoteChangesEditor.value = undefined\n resultEditor.value?.dispose()\n resultEditor.value = undefined\n\n for (const model of modelsToDispose) {\n model.dispose()\n }\n modelsToDispose = []\n\n resultCodeLensProviderDisposable?.dispose()\n resultCodeLensProviderDisposable = undefined\n jsonPointerLinkSupportDispose?.()\n jsonPointerLinkSupportDispose = undefined\n codeLensCommandDisposables.forEach((d) => d.dispose())\n codeLensCommandDisposables.length = 0\n }\n\n return {\n init,\n dispose,\n conflictsLeft,\n goToNextConflict,\n applyResolvedConflicts,\n }\n}\n"],"mappings":"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"}