Слияние
Если во время вашего редактирования сохранились другие изменения, объедините обе стороны относительно начальной версии. Всё, что можно объединить автоматически, применяется сразу; конфликтами остаются лишь пересечения. Это отдельное окно nabi-note/merge, а не wing.
| Слева | В центре | Справа |
|---|---|---|
| Версия начала редактирования | Ваш текущий документ после автоматического слияния | Версия, сохранённая во время редактирования |
| Только чтение | Можно редактировать | Только чтение |
В центре можно менять текст, форматирование, таблицы, списки и изображения как в обычном редакторе. Доступные функции определяют переданные wings. Метки и состояния конфликтов не попадают в сохранённый текст.
Полностью автоматическое слияние
Вы изменили время встречи на 11:00, другой человек — место на переговорную на 3-м этаже. Изменены разные абзацы, поэтому центральный результат содержит обе правки.
Без оставшихся конфликтов можно сразу нажать Подтвердить или сначала доработать текст в центре. Итоговый документ ниже — фактический результат, полученный callback подтверждения.
Загрузка...
Частично автоматическое слияние
Теперь вы изменили время на 11:00, другой человек — на 14:00. Разные правки одного абзаца оставляют время конфликтным. Ваше изменение ведущих на Чимина и Соён и новое место переговорная на 3-м этаже применяются автоматически.
Разрешите конфликт одним из способов:
- Слева >>: принять соответствующий фрагмент начальной версии.
- Справа <<: принять фрагмент новой сохранённой версии.
- Разрешить с текущим содержимым: оставить или изменить центр и отметить конфликт решённым.
Измените предложение со временем в центре и нажмите Разрешить с текущим содержимым. Подтверждение доступно только после разрешения всех конфликтов.
Загрузка...
Повторное редактирование решённого участка сохраняет его статус. Отмена разрешения отменяет и статус, повтор восстанавливает его. Одно удаление или редактирование текста не разрешает открытый конфликт автоматически.
Правила автоматического слияния и конфликтов
Сравниваются изменения от начального документа к вашему и от той же начальной версии к новой. Односторонние и одинаковые с обеих сторон изменения применяются автоматически, как и правки разных абзацев или строк.
Как в текстовом слиянии Git, пересечения определяются по абзацам и строкам. Даже разные слова, изменённые в одной строке, остаются конфликтом. Разные вставки в одной позиции и редактирование удалённого другой стороной содержимого также требуют решения.
Если обе стороны по-разному изменили структурный блок — таблицу, список или плавающую область, — конфликт охватывает всю структуру. Ячейки и пункты не смешиваются произвольно: выберите содержимое или исправьте центр. Пересекающиеся перемещения и правки с ненадёжно определяемой позицией тоже не применяются автоматически.
Подключение отдельного окна слияния
Передайте сохранённый NABI TREE JSON или HTML-строки в base, local, incoming. Используйте wings и параметры исходного редактора. mountMerge() создаёт центральный редактор и панель, обе исходные версии, выбор конфликтов и кнопку подтверждения.
import { wings } from 'nabi-note'
import { mountMerge } from 'nabi-note/merge'
import 'nabi-note/nabi.css'
const merge = mountMerge({
root: document.querySelector('#merge')!,
base: editingStartedDocument,
local: myEditedDocument,
incoming: latestDocument,
wings: wings().allBasic().build(),
locale: 'ru',
incomingVersion: latestVersion,
async onConfirm({ json, html, incomingVersion, signal }) {
return saveDocument({ json, html, expectedVersion: incomingVersion, signal })
},
onError(error) {
showError(error)
},
})onConfirm получает JSON, HTML для публикации, ревизию редактирования и incomingVersion полностью разрешённого текущего результата. Возврат false не завершает подтверждение. Пока ожидается Promise, редактирование, выбор конфликтов и повторное подтверждение блокируются. Демонтаж отменяет signal результата. При корейском IME-вводе подтверждение ждёт окончания композиции.
Свои действия, например выбор изображений или загрузки, подключайте как в обычном редакторе. toolbarOptions принимает панели и действия хоста, surfaceOptions — параметры поверхности редактирования.
incomingVersion возвращает переданное сервисом значение без изменений. При сохранении сервис сравнивает его с последней версией сервера. Если за время слияния сохранилась новая версия, загрузите свежий документ и начните новое слияние, не перезаписывая его устаревшим результатом.
Состояние можно читать, а подтверждение вызывать и вне окна.
const stop = merge.session.onChange((state) => {
updateRemainingCount(state.unresolvedCount)
})
const canConfirm = merge.session.getState().canConfirm
const confirmed = await merge.confirm()
stop()
merge.unmount()Читайте результат через merge.nabi.getJson() и merge.nabi.getHtml(). Не сохраняйте DOM редактора или getEditorHtml(). Для переноса лишь выбранных прежних фрагментов между двумя версиями используйте слияние по клику в Сравнении.
Подтверждение после загрузки файлов
Подключите wing upload и mountUpload() для кнопки файла, вставки и перетаскивания. sendUpload отправляет файлы серверу и возвращает { uri }. Полный пример подключения и прогресса — в Загрузке файлов.
С помощью merge.session.hold() отложите подтверждение и выбор конфликтов на время асинхронной работы, например загрузки, ещё не отражённой в документе. Вызовите возвращённую функцию при завершении, отмене или демонтаже. Снимайте задержку после вставки в документ, не сразу после отправки серверу, чтобы файл попал в итог.
import { mountUpload, wings, type UploadMount } from 'nabi-note'
import { mountMerge } from 'nabi-note/merge'
let upload: UploadMount | undefined
let releaseUpload: (() => void) | undefined
const merge = mountMerge({
root,
base,
local,
incoming,
wings: wings().allBasic().use('upload').build(),
locale: 'ru',
incomingVersion: latestVersion,
toolbarOptions: { onFiles: (files) => upload?.take(files) },
surfaceOptions: { fileSink: (files) => upload?.take(files) },
onConfirm: ({ json, html, incomingVersion, signal }) =>
saveDocument({ json, html, expectedVersion: incomingVersion, signal }),
})
upload = mountUpload({
nabi: merge.nabi,
root: merge.content,
locale: 'ru',
uploader: sendUpload,
onStart() {
releaseUpload = merge.session.hold()
},
onDone() {
releaseUpload?.()
releaseUpload = undefined
},
})
function dispose() {
upload?.unmount()
releaseUpload?.()
merge.unmount()
}merge.content — центральный DOM редактирования; merge.surface и merge.toolbar уже подключены. Не монтируйте там вторую поверхность или панель. Сначала демонтируйте дополнительные загрузки и индикаторы, затем вызовите merge.unmount().