mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
Enforce Monaco paste cap before Monaco's capture handler (#35057)
Monaco's CopyPasteController claims paste on the editor container in the capture phase before Orca's listener, so Ctrl/Cmd+V bypassed the 16 MiB cap. Listen on the editor shell instead, scoped to this editor's subtree and text focus, and only reject over-cap pastes; smaller pastes keep Monaco's atomic native paste. Remove the DOM chunked-paste plumbing (save deferral, isApplyingLargePasteRef) that could never run. Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
// Chunked pastes publish drafts only at completion; hold saves across every tab sharing the model URI to avoid writing outdated text.
|
||||
const pastesInFlight = new Map<string, number>()
|
||||
|
||||
export function beginEditorLargePaste(modelUri: string): () => void {
|
||||
pastesInFlight.set(modelUri, (pastesInFlight.get(modelUri) ?? 0) + 1)
|
||||
let ended = false
|
||||
return () => {
|
||||
if (ended) {
|
||||
return
|
||||
}
|
||||
ended = true
|
||||
const remaining = (pastesInFlight.get(modelUri) ?? 1) - 1
|
||||
if (remaining > 0) {
|
||||
pastesInFlight.set(modelUri, remaining)
|
||||
} else {
|
||||
pastesInFlight.delete(modelUri)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isEditorLargePasteInFlight(modelUri: string): boolean {
|
||||
return pastesInFlight.has(modelUri)
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// @vitest-environment happy-dom
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import type { editor } from 'monaco-editor'
|
||||
import { getDiskBaselineSignature } from '../../../../shared/disk-baseline-signature'
|
||||
import { createEditorStore } from './editor-autosave-controller-test-fixture'
|
||||
import { createEditorSaveQueue } from './editor-save-queue'
|
||||
import { toEditorModelUri } from './editor-model-uri'
|
||||
import { installMonacoEditorInputBindings } from './monaco-editor-input-bindings'
|
||||
import { MONACO_PASTE_DIRECT_MAX_BYTES } from './monaco-large-text-paste'
|
||||
import { compareDiskWritePrecondition } from '../../../../shared/file-write-disk-precondition'
|
||||
import type { GuardedWriteArgs } from './editor-save-guarded-disk-fixture'
|
||||
|
||||
vi.mock('@/store', () => ({ useAppStore: { getState: () => ({}) } }))
|
||||
vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback }))
|
||||
vi.mock('@/lib/connection-context', () => ({ getConnectionIdForFile: () => undefined }))
|
||||
vi.mock('./editor-shortcuts', () => ({
|
||||
installEditorAddReviewNoteShortcut: () => () => undefined,
|
||||
installEditorSaveShortcut: () => () => undefined,
|
||||
installMonacoEditorFindShortcut: () => () => undefined
|
||||
}))
|
||||
|
||||
const PATH = '/repo/file.ts'
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
function createSingleLineEditor(initial: string) {
|
||||
let value = initial
|
||||
let selection = { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 1 }
|
||||
const container = document.createElement('div')
|
||||
document.body.appendChild(container)
|
||||
const model = { uri: { toString: () => toEditorModelUri(PATH) }, getValue: () => value }
|
||||
const editorStub = {
|
||||
getModel: () => model,
|
||||
getContainerDomNode: () => container,
|
||||
hasTextFocus: () => true,
|
||||
getValue: () => value,
|
||||
getSelection: () => selection,
|
||||
setSelection: (next: typeof selection) => {
|
||||
selection = next
|
||||
},
|
||||
executeEdits: (_source: string, edits: { text: string }[]) => {
|
||||
const at = selection.startColumn - 1
|
||||
value = value.slice(0, at) + edits[0].text + value.slice(at)
|
||||
return true
|
||||
},
|
||||
pushUndoStop: () => true,
|
||||
addAction: () => ({ dispose: () => undefined })
|
||||
}
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: the bindings and chunked paste call only the methods stubbed above.
|
||||
return { container, editorInstance: editorStub as unknown as editor.IStandaloneCodeEditor }
|
||||
}
|
||||
|
||||
function pasteIntoGuardedTab(pasted: string) {
|
||||
const files = new Map([[PATH, 'base']])
|
||||
const writeFile = vi.fn(async (args: GuardedWriteArgs) => {
|
||||
const conflict = args.diskPrecondition
|
||||
? compareDiskWritePrecondition(
|
||||
args.diskPrecondition,
|
||||
files.get(args.filePath) ?? null,
|
||||
args.content
|
||||
)
|
||||
: null
|
||||
if (conflict === 'identical') {
|
||||
return { ok: true, unchanged: true }
|
||||
}
|
||||
if (conflict) {
|
||||
return { ok: false, diskConflict: conflict }
|
||||
}
|
||||
files.set(args.filePath, args.content)
|
||||
return { ok: true }
|
||||
})
|
||||
vi.stubGlobal('api', { fs: { writeFile } })
|
||||
const store = createEditorStore()
|
||||
store.getState().openFile({
|
||||
filePath: PATH,
|
||||
relativePath: 'file.ts',
|
||||
worktreeId: 'wt-1',
|
||||
language: 'typescript',
|
||||
mode: 'edit'
|
||||
})
|
||||
store.getState().setLastKnownDiskSignature(PATH, getDiskBaselineSignature('base'))
|
||||
store.getState().setEditorDraft(PATH, 'base')
|
||||
store.getState().markFileDirty(PATH, true)
|
||||
const queue = createEditorSaveQueue(store)
|
||||
|
||||
const { container, editorInstance } = createSingleLineEditor('base')
|
||||
const onContentChange = vi.fn((value: string) => store.getState().setEditorDraft(PATH, value))
|
||||
installMonacoEditorInputBindings({
|
||||
editorInstance,
|
||||
worktreeId: undefined,
|
||||
editorContainerRef: { current: container },
|
||||
propsRef: {
|
||||
current: { relativePath: 'file.ts', language: 'typescript', onSave: vi.fn(), onContentChange }
|
||||
},
|
||||
readOnlyRef: { current: false },
|
||||
lastSyncedContentRef: { current: 'base' },
|
||||
isApplyingLargePasteRef: { current: false },
|
||||
commentPopoverRef: { current: null },
|
||||
shouldShowMarkdownAnnotationsRef: { current: false },
|
||||
setCommentPopover: vi.fn(),
|
||||
setSelectionAnnotationTarget: vi.fn()
|
||||
})
|
||||
|
||||
const paste = new Event('paste', { cancelable: true })
|
||||
Object.defineProperty(paste, 'clipboardData', { value: { getData: () => pasted } })
|
||||
container.dispatchEvent(paste)
|
||||
|
||||
return { files, writeFile, store, queue, onContentChange }
|
||||
}
|
||||
|
||||
it('holds autosave while a chunked paste is in flight, then saves the pasted text guarded', async () => {
|
||||
const pasted = 'x'.repeat(MONACO_PASTE_DIRECT_MAX_BYTES * 2)
|
||||
const { files, writeFile, store, queue, onContentChange } = pasteIntoGuardedTab(pasted)
|
||||
|
||||
// Mid-paste the draft still holds the pre-paste text; autosave must not write it.
|
||||
expect(await queue.queueSave(store.getState().openFiles[0], 'base', 'autosave')).toBe('failed')
|
||||
expect(writeFile).not.toHaveBeenCalled()
|
||||
|
||||
await vi.waitFor(() => expect(onContentChange).toHaveBeenCalled())
|
||||
expect(await queue.queueSave(store.getState().openFiles[0], 'base', 'autosave')).toBe('written')
|
||||
expect(files.get(PATH)).toBe(`${pasted}base`)
|
||||
expect(writeFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
diskPrecondition: {
|
||||
expectedSignature: getDiskBaselineSignature('base'),
|
||||
allowMissing: false,
|
||||
requireLosslessUtf8: true
|
||||
}
|
||||
})
|
||||
)
|
||||
queue.dispose()
|
||||
})
|
||||
|
||||
it('refuses a save after a 70 KiB paste when an agent wrote the file meanwhile', async () => {
|
||||
const pasted = 'y'.repeat(70 * 1024)
|
||||
const { files, writeFile, store, queue, onContentChange } = pasteIntoGuardedTab(pasted)
|
||||
await vi.waitFor(() => expect(onContentChange).toHaveBeenCalled())
|
||||
files.set(PATH, 'agent rewrite')
|
||||
|
||||
expect(await queue.queueSave(store.getState().openFiles[0], 'base', 'user')).toBe('blocked-stale')
|
||||
expect(writeFile).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
content: `${pasted}base`,
|
||||
diskPrecondition: {
|
||||
expectedSignature: getDiskBaselineSignature('base'),
|
||||
allowMissing: true,
|
||||
requireLosslessUtf8: true
|
||||
}
|
||||
})
|
||||
)
|
||||
expect(files.get(PATH)).toBe('agent rewrite')
|
||||
expect(store.getState().openFiles[0]?.externalMutation).toBe('changed')
|
||||
queue.dispose()
|
||||
})
|
||||
@@ -24,7 +24,6 @@ import {
|
||||
} from './editor-self-write-registry'
|
||||
import { getDiskBaselineSignature } from '../../../../shared/disk-baseline-signature'
|
||||
import { editorTabFileAccess } from '@/lib/local-file-access'
|
||||
import { isEditorLargePasteInFlight } from './editor-large-paste-in-flight'
|
||||
import {
|
||||
markEditorSaveRefusal,
|
||||
settleEditorRecordAfterWrite,
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
import type { FileWriteResult } from '../../../../shared/file-write-disk-precondition'
|
||||
import {
|
||||
editorSavePrecondition,
|
||||
editorTabModelUri,
|
||||
editorSaveGrant,
|
||||
writeEditorSave,
|
||||
type EditorSaveResult,
|
||||
@@ -122,9 +120,7 @@ export function createEditorSaveQueue(store: AppStoreApi): EditorSaveQueue {
|
||||
// Explicit saves bypass autosave suspension but remain guarded; only Overwrite grants conflict consent.
|
||||
if (
|
||||
trigger === 'autosave' &&
|
||||
(isAutosaveSuspendedForFile(liveFile) ||
|
||||
isEditorLargePasteInFlight(editorTabModelUri(state, liveFile)) ||
|
||||
holds.isHeld(state, liveFile))
|
||||
(isAutosaveSuspendedForFile(liveFile) || holds.isHeld(state, liveFile))
|
||||
) {
|
||||
return 'failed'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// @vitest-environment happy-dom
|
||||
import type { editor } from 'monaco-editor'
|
||||
import { afterEach, expect, it, vi } from 'vitest'
|
||||
import { toast } from 'sonner'
|
||||
import { installMonacoEditorInputBindings } from './monaco-editor-input-bindings'
|
||||
import { MONACO_PASTE_MAX_BYTES } from './monaco-large-text-paste'
|
||||
|
||||
vi.mock('sonner', () => ({ toast: { error: vi.fn() } }))
|
||||
vi.mock('@/store', () => ({ useAppStore: { getState: vi.fn() } }))
|
||||
vi.mock('@/i18n/i18n', () => ({ translate: (_key: string, fallback: string) => fallback }))
|
||||
vi.mock('./editor-shortcuts', () => ({
|
||||
installEditorSaveShortcut: () => () => undefined,
|
||||
installMonacoEditorFindShortcut: () => () => undefined,
|
||||
installEditorAddReviewNoteShortcut: () => () => undefined
|
||||
}))
|
||||
|
||||
afterEach(() => {
|
||||
document.body.replaceChildren()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
function mountEditor() {
|
||||
const shell = document.createElement('div')
|
||||
const container = document.createElement('div')
|
||||
const editorInput = document.createElement('textarea')
|
||||
const commentInput = document.createElement('textarea')
|
||||
container.append(editorInput)
|
||||
shell.append(container, commentInput)
|
||||
document.body.append(shell)
|
||||
let value = 'base'
|
||||
// Mirrors Monaco's CopyPasteController, which claims paste on the container during construction.
|
||||
container.addEventListener(
|
||||
'paste',
|
||||
(event) => {
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
value += event.clipboardData?.getData('text/plain') ?? ''
|
||||
},
|
||||
{ capture: true }
|
||||
)
|
||||
const editorStub = {
|
||||
getModel: () => ({ uri: { toString: () => 'file:///paste.txt' } }),
|
||||
getValue: () => value,
|
||||
getContainerDomNode: () => container,
|
||||
hasTextFocus: () => true,
|
||||
addAction: () => ({ dispose: () => undefined })
|
||||
}
|
||||
const onContentChange = vi.fn()
|
||||
const binding = installMonacoEditorInputBindings({
|
||||
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: an over-limit paste is rejected after only the methods stubbed above.
|
||||
editorInstance: editorStub as unknown as editor.IStandaloneCodeEditor,
|
||||
worktreeId: undefined,
|
||||
editorContainerRef: { current: shell },
|
||||
propsRef: {
|
||||
current: {
|
||||
relativePath: 'paste.txt',
|
||||
language: 'plaintext',
|
||||
onSave: vi.fn(),
|
||||
onContentChange
|
||||
}
|
||||
},
|
||||
readOnlyRef: { current: false },
|
||||
commentPopoverRef: { current: null },
|
||||
shouldShowMarkdownAnnotationsRef: { current: false },
|
||||
setCommentPopover: vi.fn(),
|
||||
setSelectionAnnotationTarget: vi.fn()
|
||||
})
|
||||
const paste = (target: HTMLElement, text: string): ClipboardEvent => {
|
||||
const clipboardData = new DataTransfer()
|
||||
clipboardData.setData('text/plain', text)
|
||||
const event = new ClipboardEvent('paste', { bubbles: true, cancelable: true, clipboardData })
|
||||
target.dispatchEvent(event)
|
||||
return event
|
||||
}
|
||||
return { editorInput, commentInput, binding, paste, getValue: () => value, onContentChange }
|
||||
}
|
||||
|
||||
it('rejects over-limit paste before Monaco container capture handler can claim it', async () => {
|
||||
const editorMount = mountEditor()
|
||||
editorMount.paste(editorMount.editorInput, 'x'.repeat(MONACO_PASTE_MAX_BYTES + 1))
|
||||
await vi.waitFor(() => expect(toast.error).toHaveBeenCalledWith('Paste is too large.'))
|
||||
expect(editorMount.getValue()).toBe('base')
|
||||
expect(editorMount.onContentChange).not.toHaveBeenCalled()
|
||||
editorMount.binding.disposeInputBindings()
|
||||
})
|
||||
|
||||
it('leaves comment-overlay paste outside the editor route', () => {
|
||||
const editorMount = mountEditor()
|
||||
const event = editorMount.paste(editorMount.commentInput, 'x'.repeat(MONACO_PASTE_MAX_BYTES + 1))
|
||||
expect(event.defaultPrevented).toBe(false)
|
||||
editorMount.binding.disposeInputBindings()
|
||||
})
|
||||
@@ -14,8 +14,7 @@ import {
|
||||
getMonacoMarkdownSelectionAnnotationTarget,
|
||||
type MonacoMarkdownSelectionAnnotationTarget
|
||||
} from './monaco-markdown-selection-annotation'
|
||||
import { handleMonacoLargeTextPaste } from './monaco-large-text-paste'
|
||||
import { beginEditorLargePaste } from './editor-large-paste-in-flight'
|
||||
import { handleMonacoLargeTextPaste, MONACO_PASTE_MAX_BYTES } from './monaco-large-text-paste'
|
||||
import type { MarkdownCommentPopoverState } from './use-monaco-markdown-annotations'
|
||||
import type { MonacoEditorPropsRef } from './monaco-editor-mount-params'
|
||||
|
||||
@@ -25,8 +24,6 @@ type MonacoEditorInputBindingsParams = {
|
||||
editorContainerRef: MutableRefObject<HTMLDivElement | null>
|
||||
propsRef: MonacoEditorPropsRef
|
||||
readOnlyRef: MutableRefObject<boolean>
|
||||
lastSyncedContentRef: MutableRefObject<string>
|
||||
isApplyingLargePasteRef: MutableRefObject<boolean>
|
||||
commentPopoverRef: MutableRefObject<MarkdownCommentPopoverState | null>
|
||||
shouldShowMarkdownAnnotationsRef: MutableRefObject<boolean>
|
||||
setCommentPopover: Dispatch<SetStateAction<MarkdownCommentPopoverState | null>>
|
||||
@@ -45,8 +42,6 @@ export function installMonacoEditorInputBindings(params: MonacoEditorInputBindin
|
||||
editorContainerRef,
|
||||
propsRef,
|
||||
readOnlyRef,
|
||||
lastSyncedContentRef,
|
||||
isApplyingLargePasteRef,
|
||||
commentPopoverRef,
|
||||
shouldShowMarkdownAnnotationsRef,
|
||||
setCommentPopover,
|
||||
@@ -54,6 +49,7 @@ export function installMonacoEditorInputBindings(params: MonacoEditorInputBindin
|
||||
} = params
|
||||
|
||||
const editorDomNode = editorInstance.getContainerDomNode()
|
||||
const pasteCaptureNode = editorContainerRef.current ?? editorDomNode
|
||||
const cleanupSaveShortcut = installEditorSaveShortcut(editorDomNode, () => {
|
||||
const value = editorInstance.getValue()
|
||||
propsRef.current.onSave(value)
|
||||
@@ -103,22 +99,14 @@ export function installMonacoEditorInputBindings(params: MonacoEditorInputBindin
|
||||
}
|
||||
})
|
||||
const onLargeTextPaste = (event: ClipboardEvent): void => {
|
||||
let endSaveDeferral: (() => void) | null = null
|
||||
if (!event.composedPath().includes(editorDomNode)) {
|
||||
return
|
||||
}
|
||||
// Why: chunking re-serializes the document per chunk via @monaco-editor/react's onChange, so only enforce the cap.
|
||||
handleMonacoLargeTextPaste(editorInstance, event, {
|
||||
readOnly: readOnlyRef.current,
|
||||
onPasteStart: () => {
|
||||
isApplyingLargePasteRef.current = true
|
||||
const modelUri = editorInstance.getModel()?.uri.toString()
|
||||
endSaveDeferral = modelUri ? beginEditorLargePaste(modelUri) : null
|
||||
},
|
||||
directMaxBytes: MONACO_PASTE_MAX_BYTES,
|
||||
onPasteResult: (result) => {
|
||||
isApplyingLargePasteRef.current = false
|
||||
endSaveDeferral?.()
|
||||
if (result.status === 'pasted' || result.status === 'cancelled') {
|
||||
const value = editorInstance.getValue()
|
||||
lastSyncedContentRef.current = value
|
||||
propsRef.current.onContentChange(value)
|
||||
}
|
||||
if (result.status === 'rejected' && result.reason === 'too-large') {
|
||||
toast.error(
|
||||
translate(
|
||||
@@ -130,14 +118,14 @@ export function installMonacoEditorInputBindings(params: MonacoEditorInputBindin
|
||||
}
|
||||
})
|
||||
}
|
||||
editorDomNode.addEventListener('paste', onLargeTextPaste, { capture: true })
|
||||
pasteCaptureNode.addEventListener('paste', onLargeTextPaste, { capture: true })
|
||||
|
||||
return {
|
||||
disposeInputBindings: () => {
|
||||
cleanupSaveShortcut()
|
||||
cleanupFindShortcut()
|
||||
cleanupAddReviewNoteShortcut()
|
||||
editorDomNode.removeEventListener('paste', onLargeTextPaste, { capture: true })
|
||||
pasteCaptureNode.removeEventListener('paste', onLargeTextPaste, { capture: true })
|
||||
searchInFilesAction.dispose()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ export type MonacoEditorMountParams = {
|
||||
lastSyncedContentRef: MutableRefObject<string>
|
||||
contentSyncModeRef: MutableRefObject<MonacoContentSyncMode>
|
||||
isApplyingProgrammaticContentRef: MutableRefObject<boolean>
|
||||
isApplyingLargePasteRef: MutableRefObject<boolean>
|
||||
}
|
||||
decorations: {
|
||||
markdownDocLinkDecorationsRef: MutableRefObject<MarkdownDocLinkDecorationController | null>
|
||||
|
||||
@@ -212,7 +212,8 @@ export function handleMonacoLargeTextPaste(
|
||||
if (options.readOnly) {
|
||||
return { status: 'ignored', reason: 'read-only' }
|
||||
}
|
||||
if (!monacoEditor?.getModel()) {
|
||||
// Why: find/replace/rename overlay inputs sit inside the editor container.
|
||||
if (!monacoEditor?.getModel() || !monacoEditor.hasTextFocus()) {
|
||||
return { status: 'ignored', reason: 'no-editor' }
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ export type MonacoContentSyncBridge = {
|
||||
lastSyncedContentRef: MutableRefObject<string>
|
||||
contentSyncModeRef: MutableRefObject<MonacoContentSyncMode>
|
||||
isApplyingProgrammaticContentRef: MutableRefObject<boolean>
|
||||
isApplyingLargePasteRef: MutableRefObject<boolean>
|
||||
handleChange: (value: string | undefined) => void
|
||||
}
|
||||
|
||||
@@ -35,7 +34,6 @@ export function useMonacoContentSyncBridge(params: {
|
||||
|
||||
// Suppress reconciliation’s real edit events so programmatic updates do not mark the file dirty.
|
||||
const isApplyingProgrammaticContentRef = useRef(false)
|
||||
const isApplyingLargePasteRef = useRef(false)
|
||||
|
||||
const handleChange = useCallback(
|
||||
(value: string | undefined) => {
|
||||
@@ -43,11 +41,6 @@ export function useMonacoContentSyncBridge(params: {
|
||||
if (editorRef.current?.getModel()?.uri.toString() !== modelKey) {
|
||||
return
|
||||
}
|
||||
// Publish chunked pastes only on completion instead of scheduling saves for intermediate content.
|
||||
if (isApplyingLargePasteRef.current) {
|
||||
lastSyncedContentRef.current = value
|
||||
return
|
||||
}
|
||||
if (
|
||||
shouldIgnoreMonacoContentChange({
|
||||
modelKey,
|
||||
@@ -90,7 +83,6 @@ export function useMonacoContentSyncBridge(params: {
|
||||
lastSyncedContentRef,
|
||||
contentSyncModeRef,
|
||||
isApplyingProgrammaticContentRef,
|
||||
isApplyingLargePasteRef,
|
||||
handleChange
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +70,7 @@ function mountParams(
|
||||
contentRef: { current: '' },
|
||||
lastSyncedContentRef: { current: '' },
|
||||
contentSyncModeRef: { current: 'undoable' },
|
||||
isApplyingProgrammaticContentRef: { current: false },
|
||||
isApplyingLargePasteRef: { current: false }
|
||||
isApplyingProgrammaticContentRef: { current: false }
|
||||
},
|
||||
decorations: {
|
||||
markdownDocLinkDecorationsRef: { current: null },
|
||||
|
||||
@@ -77,8 +77,7 @@ function setupMount() {
|
||||
contentRef: { current: 'A (stale cache)' },
|
||||
lastSyncedContentRef: { current: 'A (stale cache)' },
|
||||
contentSyncModeRef: { current: 'undoable' },
|
||||
isApplyingProgrammaticContentRef: { current: false },
|
||||
isApplyingLargePasteRef: { current: false }
|
||||
isApplyingProgrammaticContentRef: { current: false }
|
||||
},
|
||||
decorations: {
|
||||
markdownDocLinkDecorationsRef: { current: null },
|
||||
|
||||
@@ -56,8 +56,7 @@ export function useMonacoEditorMount(params: MonacoEditorMountParams): OnMount {
|
||||
contentRef,
|
||||
lastSyncedContentRef,
|
||||
contentSyncModeRef,
|
||||
isApplyingProgrammaticContentRef,
|
||||
isApplyingLargePasteRef
|
||||
isApplyingProgrammaticContentRef
|
||||
},
|
||||
decorations: {
|
||||
markdownDocLinkDecorationsRef,
|
||||
@@ -163,8 +162,6 @@ export function useMonacoEditorMount(params: MonacoEditorMountParams): OnMount {
|
||||
editorContainerRef,
|
||||
propsRef,
|
||||
readOnlyRef,
|
||||
lastSyncedContentRef,
|
||||
isApplyingLargePasteRef,
|
||||
commentPopoverRef,
|
||||
shouldShowMarkdownAnnotationsRef,
|
||||
setCommentPopover,
|
||||
@@ -288,7 +285,6 @@ export function useMonacoEditorMount(params: MonacoEditorMountParams): OnMount {
|
||||
lastSyncedContentRef,
|
||||
contentSyncModeRef,
|
||||
isApplyingProgrammaticContentRef,
|
||||
isApplyingLargePasteRef,
|
||||
markdownDocLinkDecorationsRef,
|
||||
conflictDecorationsRef,
|
||||
commentPopoverRef,
|
||||
|
||||
Reference in New Issue
Block a user