403 lines
12 KiB
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] </span>
|
|
</div>
|
|
<div>
|
|
<span>[MarkDown] </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>
|