zotero-better-notes/addon/chrome/content/syncDiff.html

403 lines
12 KiB
HTML

<!doctype html>
<html lang="en">
<body>
<script src="chrome://__addonRef__/content/lib/js/jquery.min.js"></script>
<script src="chrome://__addonRef__/content/lib/js/dx.all.js"></script>
<link
rel="stylesheet"
type="text/css"
href="chrome://__addonRef__/content/lib/css/dx.light.compact.css"
/>
<link rel="stylesheet" href="chrome://__addonRef__/content/tooltip.css" />
<link
rel="stylesheet"
type="text/css"
href="resource://zotero/note-editor/editor.css"
/>
<style>
html,
body,
.viewport {
padding: 0;
margin: 0;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
word-wrap: break-word;
}
.viewport {
margin: 0 5px 0 5px;
}
.top-container {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
font-family: initial;
}
.header-container {
padding: 5px;
margin: 0;
height: 105px;
width: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.title-container {
padding: 5px;
margin: 0;
height: 35px;
width: 100%;
display: flex;
flex-direction: row;
overflow: hidden;
border: solid 0.5px #aaaaaa;
}
.title-text {
text-align: center;
}
.viewport-container {
padding: 0;
margin: 0;
height: calc(100% - 200px);
width: 100%;
display: flex;
flex-direction: row;
overflow: hidden;
}
.footer-container {
padding: 5px;
margin: 0;
height: 60px;
width: 100%;
display: flex;
flex-direction: row;
overflow: hidden;
}
.added {
background-color: rgb(230, 255, 236);
color: green;
}
.removed {
background-color: rgb(255, 235, 233);
color: red;
}
.added-list-item {
background-color: rgb(230, 255, 236);
}
.removed-list-item {
background-color: rgb(255, 235, 233);
}
.normal {
color: darkgray;
}
.selected-data,
.options {
margin-top: 20px;
padding: 20px;
background-color: rgba(191, 191, 191, 0.15);
}
.selected-data .caption {
font-weight: bold;
font-size: 115%;
}
.options .caption {
font-size: 18px;
font-weight: 500;
}
.option {
margin-top: 10px;
}
.option > span {
width: 120px;
display: inline-block;
}
.option > .dx-widget {
display: inline-block;
vertical-align: middle;
width: 100%;
max-width: 350px;
}
.dx-checkbox-checked .dx-checkbox-icon::before {
content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20t%3D%221670065400920%22%20class%3D%22icon%22%20viewBox%3D%220%200%201024%201024%22%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20p-id%3D%221452%22%20width%3D%2216%22%20height%3D%2216%22%3E%3Cpath%20d%3D%22M441.6%20812.8c-19.2%200-32-6.4-44.8-19.2L70.4%20467.2c-25.6-25.6-25.6-64%200-89.6%2025.6-25.6%2064-25.6%2089.6%200l281.6%20281.6%20441.6-428.8c25.6-25.6%2064-25.6%2089.6%200%2025.6%2025.6%2025.6%2064%200%2089.6l-486.4%20473.6C473.6%20806.4%20460.8%20812.8%20441.6%20812.8z%22%20p-id%3D%221453%22%20fill%3D%22%23337ab7%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E") !important;
}
img {
width: auto !important;
}
a {
color: blue;
text-decoration: underline;
}
.tool-button {
width: 75px;
max-width: 75px;
min-width: 75px;
padding: 5px;
text-align: center;
margin: 0 10px 0 10px;
}
</style>
<div class="top-container">
<div class="header-container">
<h2 style="margin: 0">Diff-Merger</h2>
<div style="display: flex">
<div style="width: -moz-fit-content; text-align: center">
<div>
<span>[Note]&nbsp;</span>
</div>
<div>
<span>[MarkDown]&nbsp;</span>
</div>
</div>
<div style="width: 100%">
<div>
<span id="note-name"></span>
</div>
<div>
<span id="md-name"></span>
</div>
</div>
<div style="width: -moz-available; text-align: right">
<div>
<span>Last Modified: </span>
<span id="note-modify"></span>
</div>
<div>
<span>Last Modified: </span>
<span id="md-modify"></span>
</div>
<div>
<span>Last Synced: </span>
<span id="sync-time"></span>
</div>
</div>
</div>
</div>
<div class="title-container">
<div class="title-text" style="width: 20%">Conflicted Changes</div>
<div class="title-text" style="width: 30%">Raw Note</div>
<div class="title-text" style="width: 50%">Rendered Note</div>
</div>
<div class="viewport-container">
<div
class="dx-viewport viewport"
id="outline-container"
style="width: calc(20% - 10px)"
>
<div class="demo-container">
<div id="list-demo">
<div class="widget-container">
<div id="simpleList"></div>
</div>
</div>
</div>
</div>
<div
class="diff-viewport viewport"
style="width: calc(30% - 10px); padding: 10px 30px 10px 30px"
></div>
<div
class="render-viewport viewport primary-editor ProseMirror"
style="width: calc(50% - 10px); padding: 10px 30px 10px 30px"
></div>
</div>
<div
class="footer-container"
style="justify-content: flex-start; padding: 10px"
>
<button
class="tool-button"
id="finish"
title="Confirm selected changes and merge. Will update MD and note."
>
Finish
</button>
<button
class="tool-button"
id="unsync"
title="Remove this note from syncing list."
>
Unsync
</button>
<button class="tool-button" id="skip" title="Skip merging this time.">
Skip
</button>
</div>
</div>
<script>
window.syncInfo = {};
window.diffData = [];
window.imageData = [];
window.io = {};
let listWidget;
let scrollRatio = 0;
function initSyncInfo() {
console.log(window.syncInfo);
document.querySelector("#note-name").innerText =
window.syncInfo.noteName;
document.querySelector("#note-modify").innerText =
window.syncInfo.noteModify;
document.querySelector("#md-name").innerText = window.syncInfo.mdName;
document.querySelector("#md-modify").innerText =
window.syncInfo.mdModify;
document.querySelector("#sync-time").innerText =
window.syncInfo.syncTime;
}
function initList() {
$(() => {
listWidget = $("#simpleList")
.dxList({
dataSource: new DevExpress.data.DataSource({
store: new DevExpress.data.ArrayStore({
key: "id",
data: window.diffData.filter(
(diff) => diff.added || diff.removed,
),
}),
}),
showSelectionControls: true,
selectionMode: "all",
onItemRendered(e) {
console.log(e, e.itemElement);
if (e.itemData) {
e.itemElement.addClass(
e.itemData.added
? "added-list-item"
: e.itemData.removed
? "removed-list-item"
: "",
);
}
},
onSelectionChanged(e) {
const addedItems = e.addedItems;
const removedItems = e.removedItems;
updateDiffRender();
let target;
if (addedItems.length === 1) {
target = addedItems[0].element;
} else if (removedItems.length === 1) {
target = removedItems[0].element;
}
if (target) {
const diffViewer = document.querySelector(".diff-viewport");
diffViewer.scrollTo(
0,
target.offsetTop - diffViewer.offsetTop,
);
}
},
})
.dxList("instance");
});
}
function getAcceptedChangeIdx() {
return listWidget.option("selectedItemKeys") || [];
}
function initDiffViewer() {
$(".diff-viewport").empty();
const frag = document.createDocumentFragment();
window.diffData.forEach((diff) => {
const span = document.createElement("span");
span.className = diff.added
? "added"
: diff.removed
? "removed"
: "normal";
span.innerText = diff.value;
frag.append(span);
diff.element = span;
});
$(".diff-viewport").append(frag);
}
function updateDiffRender(ids = undefined) {
console.log("update render");
ids = ids || getAcceptedChangeIdx();
const result = window.diffData
.filter((diff) => {
return (
(diff.added && ids.includes(diff.id)) ||
(diff.removed && !ids.includes(diff.id)) ||
(!diff.added && !diff.removed)
);
})
.map((diff) => diff.value)
.join("");
document.querySelector(".render-viewport").innerHTML = result;
document.querySelectorAll("img[data-attachment-key]").forEach((e) => {
e.src = window.imageData[e.getAttribute("data-attachment-key")];
});
window.io.result = result;
}
// https://juejin.cn/post/6844904020281147405
const syncScroller = function () {
let nodes = Array.prototype.filter.call(
arguments,
(item) => item instanceof HTMLElement,
);
let max = nodes.length;
if (!max || max === 1) return;
let sign = 0;
nodes.forEach((ele, index) => {
ele.addEventListener("scroll", function () {
if (!sign) {
sign = max - 1;
let top =
this.scrollTop / (this.scrollHeight - this.clientHeight);
let left =
this.scrollLeft / (this.scrollWidth - this.clientWidth);
for (node of nodes) {
if (node == this) continue;
node.scrollTo(
left * (node.scrollWidth - node.clientWidth),
top * (node.scrollHeight - node.clientHeight),
);
}
} else --sign;
});
});
};
window.addEventListener("DOMContentLoaded", (e) => {
const diffViewer = document.querySelector(".diff-viewport");
const renderViewer = document.querySelector(".render-viewport");
syncScroller(diffViewer, renderViewer);
document.querySelector("#finish").addEventListener("click", (e) => {
window.io.type = "finish";
window.close();
});
document.querySelector("#unsync").addEventListener("click", (e) => {
if (confirm("This note will not be synced any more. Continue?")) {
window.io.type = "unsync";
window.close();
}
});
document.querySelector("#skip").addEventListener("click", (e) => {
window.io.type = "skip";
window.close();
});
});
</script>
</body>
</html>