import { getItemKey, isPageScopeItem } from "./content-edit-target.js";
const DEBOUNCE_MS = 400;
function itemLabel(item) {
const instruction = item.instruction || "";
const text = instruction.length > 80 ? `${instruction.slice(0, 80)}…` : instruction;
if (isPageScopeItem(item)) {
return text ? `Страница: ${text}` : "Общая правка страницы";
}
return text;
}
export function createDock({ onSave, onSelect, onDraft, onDelete }) {
const root = document.createElement("div");
root.className = "content-edit-dock glass-neutral";
root.setAttribute("data-content-edit-ui", "");
root.hidden = true;
root.innerHTML = `
Список правок
Кликните блок на странице или пишите общую правку справа.
Инструкция агенту
Shift+клик по блоку — вставить ссылку в текст
`;
const listEl = root.querySelector(".content-edit-dock__items");
const emptyEl = root.querySelector(".content-edit-dock__empty");
const textarea = root.querySelector("textarea");
let debounceTimer = null;
let currentItem = null;
let suppressInput = false;
let items = [];
let selectedKey = null;
let resizeObserver = null;
let savedCaret = null;
function captureCaret() {
if (textarea.disabled) return;
savedCaret = {
start: textarea.selectionStart,
end: textarea.selectionEnd,
};
}
function syncDockHeight() {
const h = root.getBoundingClientRect().height;
document.documentElement.style.setProperty("--content-edit-dock-height", `${Math.ceil(h)}px`);
}
function observeHeight() {
if (resizeObserver) return;
resizeObserver = new ResizeObserver(syncDockHeight);
resizeObserver.observe(root);
}
function unobserveHeight() {
resizeObserver?.disconnect();
resizeObserver = null;
document.documentElement.style.removeProperty("--content-edit-dock-height");
}
function scrollSelectedIntoList() {
const btn = listEl.querySelector(".content-edit-dock__item.is-selected");
btn?.scrollIntoView({ block: "nearest", behavior: "smooth" });
}
function scheduleSave() {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
if (!currentItem) return;
onSave({ item: currentItem, instruction: textarea.value });
}, DEBOUNCE_MS);
}
function patchCurrentInstruction(raw, { draft = true } = {}) {
if (!currentItem) return;
currentItem = { ...currentItem, instruction: raw };
if (draft) onDraft?.({ item: currentItem, instruction: raw });
}
function patchCurrentItem(fields) {
if (!currentItem) return;
currentItem = { ...currentItem, ...fields };
const instruction = fields.instruction ?? currentItem.instruction ?? textarea.value;
currentItem = { ...currentItem, instruction };
onDraft?.({ item: currentItem, instruction });
}
textarea.addEventListener("input", () => {
if (suppressInput) return;
patchCurrentInstruction(textarea.value);
captureCaret();
scheduleSave();
});
textarea.addEventListener("keyup", captureCaret);
textarea.addEventListener("click", captureCaret);
textarea.addEventListener("select", captureCaret);
function renderList() {
listEl.replaceChildren();
if (!items.length) {
emptyEl.hidden = false;
return;
}
emptyEl.hidden = true;
for (const item of items) {
const li = document.createElement("li");
li.className = "content-edit-dock__row";
const btn = document.createElement("button");
btn.type = "button";
btn.className = "content-edit-dock__item";
btn.setAttribute("role", "option");
btn.dataset.itemKey = getItemKey(item);
btn.textContent = `${item.n}. ${itemLabel(item)}`;
const itemKey = btn.dataset.itemKey;
if (itemKey && itemKey === selectedKey) {
btn.classList.add("is-selected");
btn.setAttribute("aria-selected", "true");
} else {
btn.setAttribute("aria-selected", "false");
}
btn.addEventListener("click", () => onSelect?.(item));
const deleteBtn = document.createElement("button");
deleteBtn.type = "button";
deleteBtn.className = "content-edit-dock__delete";
deleteBtn.title = "Удалить";
deleteBtn.setAttribute("aria-label", `Удалить правку ${item.n}`);
deleteBtn.innerHTML = '