Initial WinUI switch.
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="language" content="english">
|
||||
<script src="./libs/jodit.min.js"></script>
|
||||
<script src="./libs/darkreader.js"></script>
|
||||
<link rel="stylesheet" href="./libs/jodit.min.css" />
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
|
||||
<style>
|
||||
.jodit-toolbar-button__trigger svg > path {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.jodit-container:not(.jodit_inline) {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-radius: initial;
|
||||
}
|
||||
|
||||
/* Hide taskbar in css. Should not be hidden from configuration, because it's used to sync state with native buttons. */
|
||||
.jodit-toolbar__box {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html, body, .jodit-container, .jodit-workplace {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<meta name="color-scheme" content="dark light">
|
||||
<textarea id="editor" name="editor"></textarea>
|
||||
|
||||
<!-- hidden input to handle image uploads -->
|
||||
<input type="file" id="imageInput" style="display:none;">
|
||||
<script src="/editor.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,130 @@
|
||||
const joditConfig = {
|
||||
"useSearch": false,
|
||||
"toolbar": true,
|
||||
"buttons": "bold,italic,underline,strikethrough,brush,ul,ol,font,fontsize,paragraph,image,link,indent,outdent,align,lineHeight,table",
|
||||
"inline": true,
|
||||
"toolbarAdaptive": false,
|
||||
"toolbarInlineForSelection": false,
|
||||
"showCharsCounter": false,
|
||||
"showWordsCounter": false,
|
||||
"showXPathInStatusbar": false,
|
||||
"link": {
|
||||
"processVideoLink": false
|
||||
},
|
||||
"disablePlugins": "add-new-line,backspace",
|
||||
"showPlaceholder": false,
|
||||
"uploader": {
|
||||
"insertImageAsBase64URI": true
|
||||
},
|
||||
"enter": "DIV"
|
||||
}
|
||||
|
||||
// This method should be called first all the time.
|
||||
function initializeJodit(fonts, defaultComposerFont, defaultComposerFontSize, defaultReaderFont, defaultReaderFontSize) {
|
||||
const fontsWithFallabckObject = fonts.reduce((acc, font) => { acc[`'${font}',Arial,sans-serif`] = font; return acc; }, {});
|
||||
const mergedConfig = {
|
||||
...joditConfig,
|
||||
controls: {
|
||||
font: {
|
||||
list: Jodit.atom(fontsWithFallabckObject)
|
||||
}
|
||||
},
|
||||
style: { font: `${defaultReaderFontSize}px ${defaultReaderFont}` },
|
||||
}
|
||||
|
||||
Jodit.plugins.add('inlineFonts', jodit => {
|
||||
jodit.events.on('afterEnter', e => {
|
||||
const current = jodit.selection.current().parentNode;
|
||||
current.style.fontFamily = `'${defaultComposerFont}',Arial,sans-serif`;
|
||||
current.style.fontSize = `${defaultComposerFontSize}px`;
|
||||
});
|
||||
});
|
||||
|
||||
// Don't add const/let/var here, it should be global
|
||||
editor = Jodit.make("#editor", mergedConfig);
|
||||
|
||||
// Handle the image input change event
|
||||
imageInput.addEventListener('change', () => {
|
||||
const file = imageInput.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
const base64Image = event.target.result;
|
||||
insertImages([{ data: base64Image, name: file.name }]);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Listeners for button events
|
||||
const disabledButtons = ["indent", "outdent"];
|
||||
const ariaPressedButtons = ["bold", "italic", "underline", "strikethrough", "ul", "ol"];
|
||||
|
||||
const alignmentButton = document.querySelector(`[ref='left']`).firstChild.firstChild;
|
||||
const alignmentObserver = new MutationObserver(function () {
|
||||
const value = alignmentButton.firstChild.getAttribute('class').split(' ')[0];
|
||||
window.chrome.webview.postMessage({ type: 'alignment', value: value });
|
||||
});
|
||||
alignmentObserver.observe(alignmentButton, { childList: true, attributes: true, attributeFilter: ["class"] });
|
||||
|
||||
const ariaObservers = ariaPressedButtons.map(button => {
|
||||
const buttonContainer = document.querySelector(`[ref='${button}']`);
|
||||
const observer = new MutationObserver(function () { pressedChanged(buttonContainer) });
|
||||
observer.observe(buttonContainer.firstChild, { attributes: true, attributeFilter: ["aria-pressed"] });
|
||||
|
||||
return observer;
|
||||
});
|
||||
|
||||
const disabledObservers = disabledButtons.map(button => {
|
||||
const buttonContainer = document.querySelector(`[ref='${button}']`);
|
||||
const observer = new MutationObserver(function () { disabledButtonChanged(buttonContainer) });
|
||||
observer.observe(buttonContainer.firstChild, { attributes: true, attributeFilter: ["disabled"] });
|
||||
|
||||
return observer;
|
||||
});
|
||||
|
||||
function pressedChanged(buttonContainer) {
|
||||
const ref = buttonContainer.getAttribute('ref');
|
||||
const value = buttonContainer.firstChild.getAttribute('aria-pressed');
|
||||
window.chrome.webview.postMessage({ type: ref, value: value });
|
||||
}
|
||||
|
||||
function disabledButtonChanged(buttonContainer) {
|
||||
const ref = buttonContainer.getAttribute('ref');
|
||||
const value = buttonContainer.firstChild.getAttribute('disabled');
|
||||
window.chrome.webview.postMessage({ type: ref, value: value });
|
||||
}
|
||||
}
|
||||
|
||||
function RenderHTML(htmlString) {
|
||||
editor.s.insertHTML(htmlString);
|
||||
editor.synchronizeValues();
|
||||
}
|
||||
|
||||
function GetHTMLContent() {
|
||||
return editor.value;
|
||||
}
|
||||
|
||||
function SetLightEditor() {
|
||||
DarkReader.disable();
|
||||
}
|
||||
|
||||
function SetDarkEditor() {
|
||||
DarkReader.enable();
|
||||
}
|
||||
|
||||
function toggleToolbar(enable) {
|
||||
const toolbar = document.querySelector('.jodit-toolbar__box');
|
||||
if (enable) {
|
||||
toolbar.style.display = 'flex';
|
||||
}
|
||||
else {
|
||||
toolbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function insertImages(imagesInfo) {
|
||||
imagesInfo.forEach(imageInfo => {
|
||||
editor.selection.insertHTML(`<img src="${imageInfo.data}" alt="${imageInfo.name}">`);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
* {
|
||||
scrollbar-color: auto !important;
|
||||
scrollbar-width: thin !important;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+5664
File diff suppressed because one or more lines are too long
Vendored
+10
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
var linkifyElement=function(e){"use strict";const t=1,n=3;function r(e,t,n){let r=n[n.length-1];e.replaceChild(r,t);for(let t=n.length-2;t>=0;t--)e.insertBefore(n[t],r),r=n[t]}function i(e,t,n){const r=[];for(let i=0;i<e.length;i++){const o=e[i];"nl"===o.t&&t.get("nl2br")?r.push(n.createElement("br")):o.isLink&&t.check(o)?r.push(t.render(o)):r.push(n.createTextNode(o.toString()))}return r}function o(l,a,c){if(!l||l.nodeType!==t)throw new Error(`Cannot linkify ${l} - Invalid DOM Node type`);if("A"===l.tagName||a.ignoreTags.indexOf(l.tagName)>=0)return l;let s=l.firstChild;for(;s;){let d,u,f;switch(s.nodeType){case t:o(s,a,c);break;case n:if(d=s.nodeValue,u=e.tokenize(d),0===u.length||1===u.length&&"text"===u[0].t)break;f=i(u,a,c),r(l,s,f),s=f[f.length-1]}s=s.nextSibling}return l}function l(e){return t=>{let{tagName:n,attributes:r,content:i,eventListeners:o}=t;const l=e.createElement(n);for(const e in r)l.setAttribute(e,r[e]);if(o&&l.addEventListener)for(const e in o)l.addEventListener(e,o[e]);return l.appendChild(e.createTextNode(i)),l}}function a(t,n,r){void 0===n&&(n=null),void 0===r&&(r=null);try{r=r||document||window&&window.document||global&&global.document}catch(e){}if(!r)throw new Error("Cannot find document implementation. If you are in a non-browser environment like Node.js, pass the document implementation as the third argument to linkifyElement.");return o(t,new e.Options(n,l(r)),r)}return a.helper=o,a.getDefaultRender=l,a.normalize=(t,n)=>new e.Options(t,l(n)),a}(linkify);
|
||||
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,116 @@
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="./global.css" />
|
||||
<script src="./libs/darkreader.js"></script>
|
||||
|
||||
<script src="./libs/linkify.min.js"></script>
|
||||
<script src="./libs/linkify-element.min.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
margin: 0px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
#readerDiv {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
a.wino-plain-link {
|
||||
color: inherit !important;
|
||||
text-decoration: underline dotted !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<meta name="color-scheme" content="dark light" />
|
||||
<script>
|
||||
var _htmlString = "";
|
||||
var _shouldLinkifyText = true;
|
||||
|
||||
// Called when rendering a new email for the first time
|
||||
function RenderHTML(htmlString, shouldLinkifyText = true) {
|
||||
|
||||
// Reset scroll to top
|
||||
window.scroll(0, 0);
|
||||
|
||||
// Clear any existing selection
|
||||
window.getSelection().removeAllRanges();
|
||||
|
||||
_htmlString = htmlString;
|
||||
_shouldLinkifyText = shouldLinkifyText;
|
||||
|
||||
internalRenderHTML(htmlString);
|
||||
}
|
||||
|
||||
// Called to render or refresh the email
|
||||
function internalRenderHTML(htmlString) {
|
||||
var containerDiv = document.getElementById("readerDiv");
|
||||
try {
|
||||
containerDiv.innerHTML = htmlString;
|
||||
|
||||
// Linkify plain text links if enabled
|
||||
if (_shouldLinkifyText) {
|
||||
linkifyElement(
|
||||
containerDiv,
|
||||
{ className: "wino-plain-link" },
|
||||
document
|
||||
);
|
||||
}
|
||||
|
||||
// Remove !important from inline styles if dark mode is enabled
|
||||
if (
|
||||
document.documentElement.getAttribute("data-theme") ===
|
||||
"dark"
|
||||
) {
|
||||
removeImportantFromInlineStyles();
|
||||
}
|
||||
} catch (e) {
|
||||
containerDiv.innerHTML = htmlString;
|
||||
}
|
||||
}
|
||||
|
||||
function ChangeFontFamily(fontFamily) {
|
||||
var containerDiv = document.getElementById("readerDiv");
|
||||
containerDiv.style.fontFamily = fontFamily;
|
||||
}
|
||||
|
||||
function ChangeFontSize(size) {
|
||||
var containerDiv = document.getElementById("readerDiv");
|
||||
containerDiv.style.fontSize = size;
|
||||
}
|
||||
|
||||
function SetLightEditor() {
|
||||
document.documentElement.setAttribute("data-theme", "light");
|
||||
DarkReader.disable();
|
||||
|
||||
internalRenderHTML(_htmlString);
|
||||
}
|
||||
|
||||
function SetDarkEditor() {
|
||||
document.documentElement.setAttribute("data-theme", "dark");
|
||||
DarkReader.enable();
|
||||
|
||||
internalRenderHTML(_htmlString);
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function removeImportantFromInlineStyles() {
|
||||
var allElements = document.querySelectorAll("*");
|
||||
allElements.forEach(function (element) {
|
||||
var style = element.getAttribute("style");
|
||||
if (style) {
|
||||
var newStyle = style.replace(/!important/g, "");
|
||||
element.setAttribute("style", newStyle);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<div id="readerDiv"></div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user