Migrate from Quill to Jodit (#264)

* Refactored JS folder.
Removed useless files

* Add border to signature webview ( Fix for light theme)

* migrated quill to jodit(links not working)

* Removed quill subfolder

* removed table styles and fixed trigger color

* disable addaptive toolbar

* Remove direction button

* MinHeight, Toolbar toggle, style for combobox

* Added reference mail to replies and forward

* Command bar in compose page
Fixed align behaviour with Jodit

* Fix theme toggle in composer

* switch cc and to in mail chain

* default selected value for aligment

* make CC/To/From to be mailto links

* Added drag and drop for images
Fixed dropzones visual states
Corrected border radius
Fixed null reference exception when event dispatched when chromium still not initialized
This commit is contained in:
Tiktack
2024-07-07 18:04:53 +02:00
committed by GitHub
parent 02cd8ed7ae
commit d6b3240506
36 changed files with 6444 additions and 23029 deletions

View File

@@ -8,7 +8,7 @@ namespace Wino.Core.Domain.Interfaces
{
string GetWebAuthenticationBrokerUri();
Task<string> GetMimeMessageStoragePath();
Task<string> GetQuillEditorBundlePathAsync();
Task<string> GetEditorBundlePathAsync();
Task LaunchFileAsync(string filePath);
Task LaunchUriAsync(Uri uri);
bool IsAppRunning();

View File

@@ -0,0 +1,9 @@
namespace Wino.Core.Domain.Models.Requests
{
// Used to pass messages from the webview to the app.
public class WebViewMessage
{
public string type { get; set; }
public string value { get; set; }
}
}

View File

@@ -52,6 +52,7 @@
"ClipboardTextCopyFailed_Message": "Failed to copy {0} to clipboard.",
"ComposerToPlaceholder": "click enter to input addresses",
"ComposerAttachmentsDropZone_Message": "Drop your files here",
"ComposerImagesDropZone_Message": "Drop your images here",
"ComposerAttachmentsDragDropAttach_Message": "Attach",
"CustomThemeBuilder_AccentColorDescription": "Set custom accent color if you wish. Not selecting a color will use your Windows accent color.",
"CustomThemeBuilder_AccentColorTitle": "Accent color",

File diff suppressed because it is too large Load Diff

View File

@@ -77,11 +77,11 @@ namespace Wino.Services
return new GoogleAuthorizationRequest(state, code_verifier, code_challenge);
}
public async Task<string> GetQuillEditorBundlePathAsync()
public async Task<string> GetEditorBundlePathAsync()
{
if (string.IsNullOrEmpty(_editorBundlePath))
{
var editorFileFromBundle = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///JS/Quill/full.html"))
var editorFileFromBundle = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///JS/editor.html"))
.AsTask()
.ConfigureAwait(false);

View File

@@ -670,16 +670,13 @@ namespace Wino.Core.Services
}
message.Headers.Add("Thread-Topic", referenceMessage.Subject);
}
var previewer = new HtmlTextPreviewer();
builder.HtmlBody = CreateHtmlForReferencingMessage(referenceMessage);
}
if (reason == DraftCreationReason.Forward)
{
var visitor = _mimeFileService.CreateHTMLPreviewVisitor(referenceMessage, string.Empty);
visitor.Visit(referenceMessage);
builder.HtmlBody = visitor.HtmlBody;
builder.HtmlBody = CreateHtmlForReferencingMessage(referenceMessage);
}
// Append signatures if needed.
@@ -695,11 +692,11 @@ namespace Wino.Core.Services
if (string.IsNullOrWhiteSpace(builder.HtmlBody))
{
builder.HtmlBody = @$"<html><br><br>{signature.HtmlBody}</html>";
builder.HtmlBody = $"<br><br><br>{signature.HtmlBody}";
}
else
{
builder.HtmlBody += @$"{signature.HtmlBody}";
builder.HtmlBody = $"<br><br><br>{signature.HtmlBody}" + builder.HtmlBody;
}
}
}
@@ -772,6 +769,37 @@ namespace Wino.Core.Services
}
return message;
// Generates html representation of To/Cc/From/Time and so on from referenced message.
string CreateHtmlForReferencingMessage(MimeMessage referenceMessage)
{
var htmlMimeInfo = string.Empty;
// Separation Line
htmlMimeInfo += "<br><br><hr style='display:inline-block;width:100%' tabindex='-1'>";
var visitor = _mimeFileService.CreateHTMLPreviewVisitor(referenceMessage, string.Empty);
visitor.Visit(referenceMessage);
htmlMimeInfo += $"""
<div id="divRplyFwdMsg" dir="ltr">
<font face="Calibri, sans-serif" style="font-size: 11pt;" color="#000000">
<b>From:</b> {ParticipantsToHtml(referenceMessage.From)}<br>
<b>Sent:</b> {referenceMessage.Date.ToLocalTime()}<br>
<b>To:</b> {ParticipantsToHtml(referenceMessage.To)}<br>
{(referenceMessage.Cc.Count > 0 ? $"<b>Cc:</b> {ParticipantsToHtml(referenceMessage.Cc)}<br>" : string.Empty)}
<b>Subject:</b> {referenceMessage.Subject}
</font>
<div>&nbsp;</div>
{visitor.HtmlBody}
</div>
""";
return htmlMimeInfo;
}
static string ParticipantsToHtml(InternetAddressList internetAddresses) =>
string.Join("; ", internetAddresses.Mailboxes
.Select(x => $"{x.Name ?? Translator.UnknownSender} &lt;<a href=\"mailto:{x.Address ?? Translator.UnknownAddress}\">{x.Address ?? Translator.UnknownAddress}</a>&gt;"));
}
public async Task<bool> MapLocalDraftAsync(Guid accountId, Guid localDraftCopyUniqueId, string newMailCopyId, string newDraftId, string newThreadId)

View File

@@ -72,7 +72,10 @@ namespace Wino.Mail.ViewModels
private bool isDraggingOverComposerGrid;
[ObservableProperty]
private bool isDraggingOverDropZone;
private bool isDraggingOverFilesDropZone;
[ObservableProperty]
private bool isDraggingOverImagesDropZone;
public ObservableCollection<MailAttachmentViewModel> IncludedAttachments { get; set; } = new ObservableCollection<MailAttachmentViewModel>();

View File

@@ -137,6 +137,9 @@
<PathIcon Data="{StaticResource OrderedListPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarSeparator />
<AppBarButton
x:Name="DecreaseIndentButton"
Width="48"
@@ -157,22 +160,14 @@
<PathIcon Data="{StaticResource IncreaseIndentPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarSeparator />
<AppBarToggleButton
x:Name="DirectionButton"
Width="48"
Click="DirectionButtonClicked"
Label="Direction"
ToolTipService.ToolTip="Direction">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource ParagraphPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarElementContainer VerticalAlignment="Center">
<ComboBox x:Name="AlignmentListView" SelectionChanged="AlignmentChanged">
<ComboBoxItem Tag="left">
<ComboBox
x:Name="AlignmentListView"
Background="Transparent"
BorderBrush="Transparent"
SelectionChanged="AlignmentChanged">
<ComboBoxItem IsSelected="True" Tag="left">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource AlignLeftPathIcon}" />
@@ -238,43 +233,30 @@
<PathIcon Data="{StaticResource EmojiPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton
x:Name="LinkButton"
<AppBarToggleButton
x:Name="WebviewToolBarButton"
Width="48"
Click="LinkButtonClicked"
FrameworkElement.AllowFocusOnInteraction="True"
Label="Add HyperLink"
ToolTipService.ToolTip="Add HyperLink">
<AppBarButton.Flyout>
<Flyout x:Name="HyperlinkFlyout">
<StackPanel Width="250" Spacing="6">
<TextBox x:Name="HyperlinkTextBox" Header="Text" />
<TextBox
x:Name="LinkUrlTextBox"
Header="Link"
PlaceholderText="https://" />
<Button
x:Name="AddHyperlinkButton"
Margin="0,6"
HorizontalAlignment="Stretch"
Click="HyperlinkAddClicked"
Content="{x:Bind domain:Translator.AddHyperlink}" />
</StackPanel>
</Flyout>
</AppBarButton.Flyout>
<AppBarButton.Icon>
<PathIcon Data="{StaticResource AddLinkPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
Click="WebViewToggleButtonClicked"
Label="Webview ToolBar"
ToolTipService.ToolTip="Webview ToolBar">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource WebviewToolBarPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
</CommandBar>
<Grid
<Border
Grid.Row="1"
Margin="0,6,0,0"
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="3">
<Grid Background="White" Visibility="{x:Bind IsComposerDarkMode, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}" />
<Grid CornerRadius="3">
<Grid Background="White" Visibility="{x:Bind IsComposerDarkMode, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}" />
<muxc:WebView2 x:Name="Chromium" />
</Grid>
<muxc:WebView2 x:Name="Chromium" />
</Grid>
</Border>
</Grid>
</Grid>
</ContentDialog>

View File

@@ -10,6 +10,7 @@ using Windows.UI.Xaml.Controls;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Requests;
using Wino.Views.Settings;
namespace Wino.Dialogs
@@ -71,9 +72,9 @@ namespace Wino.Dialogs
_getHTMLBodyFunction = new Func<Task<string>>(async () =>
{
var quillContent = await InvokeScriptSafeAsync("GetHTMLContent();");
var editorContent = await InvokeScriptSafeAsync("GetHTMLContent();");
return JsonConvert.DeserializeObject<string>(quillContent);
return JsonConvert.DeserializeObject<string>(editorContent);
});
var underlyingThemeService = App.Current.Services.GetService<IUnderlyingThemeService>();
@@ -121,57 +122,44 @@ namespace Wino.Dialogs
Hide();
}
private async void HyperlinkAddClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync($"addHyperlink('{LinkUrlTextBox.Text}')");
LinkUrlTextBox.Text = string.Empty;
HyperlinkFlyout.Hide();
}
private async void BoldButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('boldButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('bold')");
}
private async void ItalicButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('italicButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('italic')");
}
private async void UnderlineButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('underlineButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('underline')");
}
private async void StrokeButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('strikeButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('strikethrough')");
}
private async void BulletListButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('bulletListButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('insertunorderedlist')");
}
private async void OrderedListButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('orderedListButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('insertorderedlist')");
}
private async void IncreaseIndentClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('increaseIndentButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('indent')");
}
private async void DecreaseIndentClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('decreaseIndentButton').click();");
}
private async void DirectionButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('directionButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('outdent')");
}
private async void AlignmentChanged(object sender, SelectionChangedEventArgs e)
@@ -182,16 +170,16 @@ namespace Wino.Dialogs
switch (alignment)
{
case "left":
await InvokeScriptSafeAsync("document.getElementById('ql-align-left').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifyleft')");
break;
case "center":
await InvokeScriptSafeAsync("document.getElementById('ql-align-center').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifycenter')");
break;
case "right":
await InvokeScriptSafeAsync("document.getElementById('ql-align-right').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifyright')");
break;
case "justify":
await InvokeScriptSafeAsync("document.getElementById('ql-align-justify').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifyfull')");
break;
}
}
@@ -218,19 +206,22 @@ namespace Wino.Dialogs
{
return await Chromium.ExecuteScriptAsync(function);
}
catch { }
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return string.Empty;
}
private async void AddImageClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('addImageButton').click();");
await InvokeScriptSafeAsync("imageInput.click();");
}
private async Task FocusEditorAsync()
{
await InvokeScriptSafeAsync("quill.focus();");
await InvokeScriptSafeAsync("editor.selection.focus();");
Chromium.Focus(FocusState.Keyboard);
Chromium.Focus(FocusState.Programmatic);
@@ -254,12 +245,10 @@ namespace Wino.Dialogs
return string.Empty;
}
private async void LinkButtonClicked(object sender, RoutedEventArgs e)
private async void WebViewToggleButtonClicked(object sender, RoutedEventArgs e)
{
// Get selected text from Quill.
HyperlinkTextBox.Text = await TryGetSelectedTextAsync();
HyperlinkFlyout.ShowAt(LinkButton);
var enable = WebviewToolBarButton.IsChecked == true ? "true" : "false";
await InvokeScriptSafeAsync($"toggleToolbar('{enable}');");
}
private async Task UpdateEditorThemeAsync()
@@ -298,10 +287,10 @@ namespace Wino.Dialogs
private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
{
var editorBundlePath = (await _nativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty);
var editorBundlePath = (await _nativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.example", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
Chromium.Source = new Uri("https://app.example/full.html");
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
Chromium.Source = new Uri("https://app.editor/editor.html");
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded;
@@ -320,46 +309,52 @@ namespace Wino.Dialogs
private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var change = JsonConvert.DeserializeObject<string>(args.WebMessageAsJson);
var change = JsonConvert.DeserializeObject<WebViewMessage>(args.WebMessageAsJson);
bool isEnabled = change.EndsWith("ql-active");
if (change.StartsWith("ql-bold"))
BoldButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-italic"))
ItalicButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-underline"))
UnderlineButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-strike"))
StrokeButton.IsChecked = isEnabled;
else if (change.StartsWith("orderedListButton"))
OrderedListButton.IsChecked = isEnabled;
else if (change.StartsWith("bulletListButton"))
BulletListButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-direction"))
DirectionButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-align-left"))
if (change.type == "bold")
{
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 0;
AlignmentListView.SelectionChanged += AlignmentChanged;
BoldButton.IsChecked = change.value == "true";
}
else if (change.StartsWith("ql-align-center"))
else if (change.type == "italic")
{
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 1;
AlignmentListView.SelectionChanged += AlignmentChanged;
ItalicButton.IsChecked = change.value == "true";
}
else if (change.StartsWith("ql-align-right"))
else if (change.type == "underline")
{
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 2;
AlignmentListView.SelectionChanged += AlignmentChanged;
UnderlineButton.IsChecked = change.value == "true";
}
else if (change.StartsWith("ql-align-justify"))
else if (change.type == "strikethrough")
{
StrokeButton.IsChecked = change.value == "true";
}
else if (change.type == "ol")
{
OrderedListButton.IsChecked = change.value == "true";
}
else if (change.type == "ul")
{
BulletListButton.IsChecked = change.value == "true";
}
else if (change.type == "indent")
{
IncreaseIndentButton.IsEnabled = change.value == "disabled" ? false : true;
}
else if (change.type == "outdent")
{
DecreaseIndentButton.IsEnabled = change.value == "disabled" ? false : true;
}
else if (change.type == "alignment")
{
var parsedValue = change.value switch
{
"jodit-icon_left" => 0,
"jodit-icon_center" => 1,
"jodit-icon_right" => 2,
"jodit-icon_justify" => 3,
_ => 0
};
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 3;
AlignmentListView.SelectedIndex = parsedValue;
AlignmentListView.SelectionChanged += AlignmentChanged;
}
}

View File

@@ -1,278 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="language" content="english">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="https://app.example/katex.min.css" />
<link rel="stylesheet" href="https://app.example/monokai-sublime.min.css" />
<link rel="stylesheet" href="https://app.example/quill.snow.css" />
<style>
body >
/* #standalone-container {
margin: 50px auto;
height: 100%;
} */
#editor-container {
height: 100%;
}
#wino-original-message-container {
border: solid #636e72;
margin-top: 5px;
}
</style>
</head>
<body bgcolor="white">
<div id="standalone-container">
<!-- remove display none to enable toolbar -->
<div id="toolbar-container" style="display: none;">
<span class="ql-formats">
<!-- <button id="ql-font-monospace" class="ql-font" value="monospace" />
<button id="ql-font-serif" class="ql-font" value="serif" />
<button id="ql-font-sans-serif" class="ql-font" /> -->
<select class="ql-font"></select>
<select class="ql-size"></select>
</span>
<span class="ql-formats">
<button id="boldButton" class="ql-bold"></button>
<button id="italicButton" class="ql-italic"></button>
<button id="underlineButton" class="ql-underline"></button>
<button id="strikeButton" class="ql-strike"></button>
</span>
<span class="ql-formats">
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
<span class="ql-formats">
<button class="ql-script" value="sub"></button>
<button class="ql-script" value="super"></button>
</span>
<span class="ql-formats">
<button class="ql-header" value="1"></button>
<button class="ql-header" value="2"></button>
<button class="ql-blockquote"></button>
<button class="ql-code-block"></button>
</span>
<span class="ql-formats">
<button id="orderedListButton" class="ql-list" value="ordered"></button>
<button id="bulletListButton" class="ql-list" value="bullet"></button>
<button id="decreaseIndentButton" class="ql-indent" value="-1"></button>
<button id="increaseIndentButton" class="ql-indent" value="+1"></button>
</span>
<span class="ql-formats">
<button id="directionButton" class="ql-direction" value="rtl"></button>
<button id="ql-align-left" class="ql-align" value=""></button>
<button id="ql-align-center" class="ql-align" value="center"></button>
<button id="ql-align-right" class="ql-align" value="right"></button>
<button id="ql-align-justify" class="ql-align" value="justify"></button>
<!--<select id="alignmentButton" class="ql-align"></select>-->
</span>
<span class="ql-formats">
<button class="ql-link"></button>
<button id=addImageButton class="ql-image"></button>
<button class="ql-video"></button>
<button class="ql-formula"></button>
</span>
<span class="ql-formats">
<button class="ql-clean"></button>
</span>
</div>
</div>
<div id="editor-container" style="background-color: white;">
<br><br>Sent from <a href="https://github.com/bkaankose/Wino-Mail/">Wino Mail</a> for Windows
</div>
<!-- <div id="mail-container-parent" hidden="true" style="border: solid #636e72; padding: 10px; margin-top: 5px; margin-right: 16px; margin-bottom: 5px; margin-left: 5px;">
<center>
Original message<br>
</center>
<div id="mail-container" style="margin-top: 5px;">
</div>
</div> -->
<script src="https://app.example/katex.min.js"></script>
<script src="https://app.example/highlight.min.js"></script>
<!-- <script src="../quill.min.js"></script>
<script src="../image-resize.min.js"></script> -->
<script src="https://app.example/quill.min.js"></script>
<script src="https://app.example/image-resize.min.js"></script>
<script>
// configure Quill to use inline styles so the email's format properly
var resizer = Quill.import('modules/imageResize');
Quill.register(resizer, true);
var DirectionAttribute = Quill.import('attributors/attribute/direction');
Quill.register(DirectionAttribute, true);
var AlignClass = Quill.import('attributors/class/align');
Quill.register(AlignClass, true);
var BackgroundClass = Quill.import('attributors/class/background');
Quill.register(BackgroundClass, true);
var ColorClass = Quill.import('attributors/class/color');
Quill.register(ColorClass, true);
var DirectionClass = Quill.import('attributors/class/direction');
Quill.register(DirectionClass, true);
var FontClass = Quill.import('attributors/class/font');
Quill.register(FontClass, true);
var SizeClass = Quill.import('attributors/class/size');
Quill.register(SizeClass, true);
var AlignStyle = Quill.import('attributors/style/align');
Quill.register(AlignStyle, true);
var BackgroundStyle = Quill.import('attributors/style/background');
Quill.register(BackgroundStyle, true);
var ColorStyle = Quill.import('attributors/style/color');
Quill.register(ColorStyle, true);
var DirectionStyle = Quill.import('attributors/style/direction');
Quill.register(DirectionStyle, true);
var FontStyle = Quill.import('attributors/style/font');
Quill.register(FontStyle, true);
var SizeStyle = Quill.import('attributors/style/size');
Quill.register(SizeStyle, true);
var quill = new Quill('#editor-container', {
modules: {
toolbar: '#toolbar-container',
imageResize: {}
},
placeholder: '',
theme: 'snow'
});
var boldButton = document.getElementById('boldButton');
var italicButton = document.getElementById('italicButton');
var underlineButton = document.getElementById('underlineButton');
var strikeButton = document.getElementById('strikeButton');
var orderedListButton = document.getElementById('orderedListButton');
var bulletListButton = document.getElementById('bulletListButton');
var directionButton = document.getElementById('directionButton');
var alignLeftButton = document.getElementById('ql-align-left');
var alignCenterButton = document.getElementById('ql-align-center');
var alignRightButton = document.getElementById('ql-align-right');
var alignJustifyButton = document.getElementById('ql-align-justify');
// The mutation observer
var boldObserver = new MutationObserver(function() { classChanged(boldButton); });
boldObserver.observe(boldButton, { attributes: true, attributeFilter: ["class"]});
var italicObserver = new MutationObserver(function() { classChanged(italicButton); });
italicObserver.observe(italicButton, { attributes: true, attributeFilter: ["class"] });
var underlineObserver = new MutationObserver(function () { classChanged(underlineButton); });
underlineObserver.observe(underlineButton, { attributes: true, attributeFilter: ["class"] });
var strikeObserver = new MutationObserver(function () { classChanged(strikeButton); });
strikeObserver.observe(strikeButton, { attributes: true, attributeFilter: ["class"] });
var orderedListObserver = new MutationObserver(function () { classAndValueChanged(orderedListButton); });
orderedListObserver.observe(orderedListButton, { attributes: true, attributeFilter: ["class"] });
var bulletListObserver = new MutationObserver(function () { classAndValueChanged(bulletListButton); });
bulletListObserver.observe(bulletListButton, { attributes: true, attributeFilter: ["class"] });
var directionObserver = new MutationObserver(function () { classChanged(directionButton); });
directionObserver.observe(directionButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserver = new MutationObserver(function () { alignmentDataValueChanged(alignLeftButton); });
alignmentObserver.observe(alignLeftButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserverCenter = new MutationObserver(function () { alignmentDataValueChanged(alignCenterButton); });
alignmentObserverCenter.observe(alignCenterButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserverRight = new MutationObserver(function () { alignmentDataValueChanged(alignRightButton); });
alignmentObserverRight.observe(alignRightButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserverJustify = new MutationObserver(function () { alignmentDataValueChanged(alignJustifyButton); });
alignmentObserverJustify.observe(alignJustifyButton, { attributes: true, attributeFilter: ["class"] });
function classChanged(button) {
window.external.notify(`${button.className}`);
}
function classAndValueChanged(button) {
window.external.notify(`${button.id} ${button.className}`);
}
function alignmentDataValueChanged(button) {
if (button.className.endsWith('ql-active'))
window.external.notify(`${button.id}`);
}
function RenderHTML(htmlString) {
quill.root.innerHTML = htmlString;
// quill.clipboard.dangerouslyPasteHTML(htmlString);
// const delta = quill.clipboard.convert(htmlString)
// quill.setContents(delta, 'silent')
// document.getElementById('mail-container-parent').hidden = false;
// document.getElementById('editor-container').innerHTML = htmlString;
}
// function ShowToolbar() {
// document.getElementById('editor-container').style.display = 'block';
// quill.focus();
// }
// function HideToolbar() {
// document.getElementById('editor-container').style.display = 'none';
// }
function getSelectedText() {
var range = quill.getSelection();
if (range)
{
if (range.length == 0)
{
}
else
{
return quill.getText(range.index, range.length);
}
}
}
function addHyperlink(url)
{
var range = quill.getSelection();
if (range)
{
quill.formatText(range.index, range.length, 'link', url);
quill.setSelection(0,0);
}
}
</script>
</body>
</html>

View File

@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="language" content="english">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="https://app.example/katex.min.css" />
<link rel="stylesheet" href="https://app.example/monokai-sublime.min.css" />
<link rel="stylesheet" href="https://app.example/quill.snow.css" />
<link rel="stylesheet" href="https://app.example/global.css" />
<style>
body >
/* #standalone-container {
margin: 50px auto;
height: 100%;
} */
#editor-container {
height: 100%;
}
#wino-original-message-container {
border: solid #636e72;
margin-top: 5px;
}
</style>
</head>
<body>
<meta name="color-scheme" content="dark light">
<div id="standalone-container">
<!-- remove display none to enable toolbar -->
<div id="toolbar-container" style="display: none;">
<span class="ql-formats">
<!-- <button id="ql-font-monospace" class="ql-font" value="monospace" />
<button id="ql-font-serif" class="ql-font" value="serif" />
<button id="ql-font-sans-serif" class="ql-font" /> -->
<select class="ql-font"></select>
<select class="ql-size"></select>
</span>
<span class="ql-formats">
<button id="boldButton" class="ql-bold"></button>
<button id="italicButton" class="ql-italic"></button>
<button id="underlineButton" class="ql-underline"></button>
<button id="strikeButton" class="ql-strike"></button>
</span>
<span class="ql-formats">
<select class="ql-color"></select>
<select class="ql-background"></select>
</span>
<span class="ql-formats">
<button class="ql-script" value="sub"></button>
<button class="ql-script" value="super"></button>
</span>
<span class="ql-formats">
<button class="ql-header" value="1"></button>
<button class="ql-header" value="2"></button>
<button class="ql-blockquote"></button>
<button class="ql-code-block"></button>
</span>
<span class="ql-formats">
<button id="orderedListButton" class="ql-list" value="ordered"></button>
<button id="bulletListButton" class="ql-list" value="bullet"></button>
<button id="decreaseIndentButton" class="ql-indent" value="-1"></button>
<button id="increaseIndentButton" class="ql-indent" value="+1"></button>
</span>
<span class="ql-formats">
<button id="directionButton" class="ql-direction" value="rtl"></button>
<button id="ql-align-left" class="ql-align" value=""></button>
<button id="ql-align-center" class="ql-align" value="center"></button>
<button id="ql-align-right" class="ql-align" value="right"></button>
<button id="ql-align-justify" class="ql-align" value="justify"></button>
<!--<select id="alignmentButton" class="ql-align"></select>-->
</span>
<span class="ql-formats">
<button class="ql-link"></button>
<button id=addImageButton class="ql-image"></button>
<button class="ql-video"></button>
<button class="ql-formula"></button>
</span>
<span class="ql-formats">
<button class="ql-clean"></button>
</span>
</div>
</div>
<!--style="background-color: white;"-->
<div id="editor-container">
<!--<br><br>Sent from <a href="https://github.com/bkaankose/Wino-Mail/">Wino Mail</a> for Windows-->
</div>
<!-- <div id="mail-container-parent" hidden="true" style="border: solid #636e72; padding: 10px; margin-top: 5px; margin-right: 16px; margin-bottom: 5px; margin-left: 5px;">
<center>
Original message<br>
</center>
<div id="mail-container" style="margin-top: 5px;">
</div>
</div> -->
<script src="https://app.example/katex.min.js"></script>
<script src="https://app.example/highlight.min.js"></script>
<!-- <script src="../quill.min.js"></script>
<script src="../image-resize.min.js"></script> -->
<script src="https://app.example/quill.min.js"></script>
<script src="https://app.example/image-resize.min.js"></script>
<script src="https://app.example/darkreader.js"></script>
<script>
// configure Quill to use inline styles so the email's format properly
var resizer = Quill.import('modules/imageResize');
Quill.register(resizer, true);
var DirectionAttribute = Quill.import('attributors/attribute/direction');
Quill.register(DirectionAttribute, true);
var AlignClass = Quill.import('attributors/class/align');
Quill.register(AlignClass, true);
var BackgroundClass = Quill.import('attributors/class/background');
Quill.register(BackgroundClass, true);
var ColorClass = Quill.import('attributors/class/color');
Quill.register(ColorClass, true);
var DirectionClass = Quill.import('attributors/class/direction');
Quill.register(DirectionClass, true);
var FontClass = Quill.import('attributors/class/font');
Quill.register(FontClass, true);
var SizeClass = Quill.import('attributors/class/size');
Quill.register(SizeClass, true);
var AlignStyle = Quill.import('attributors/style/align');
Quill.register(AlignStyle, true);
var BackgroundStyle = Quill.import('attributors/style/background');
Quill.register(BackgroundStyle, true);
var ColorStyle = Quill.import('attributors/style/color');
Quill.register(ColorStyle, true);
var DirectionStyle = Quill.import('attributors/style/direction');
Quill.register(DirectionStyle, true);
var FontStyle = Quill.import('attributors/style/font');
Quill.register(FontStyle, true);
var SizeStyle = Quill.import('attributors/style/size');
Quill.register(SizeStyle, true);
var quill = new Quill('#editor-container', {
modules: {
toolbar: '#toolbar-container',
imageResize: {}
},
placeholder: '',
theme: 'snow'
});
var boldButton = document.getElementById('boldButton');
var italicButton = document.getElementById('italicButton');
var underlineButton = document.getElementById('underlineButton');
var strikeButton = document.getElementById('strikeButton');
var orderedListButton = document.getElementById('orderedListButton');
var bulletListButton = document.getElementById('bulletListButton');
var directionButton = document.getElementById('directionButton');
var alignLeftButton = document.getElementById('ql-align-left');
var alignCenterButton = document.getElementById('ql-align-center');
var alignRightButton = document.getElementById('ql-align-right');
var alignJustifyButton = document.getElementById('ql-align-justify');
// The mutation observer
var boldObserver = new MutationObserver(function () { classChanged(boldButton); });
boldObserver.observe(boldButton, { attributes: true, attributeFilter: ["class"] });
var italicObserver = new MutationObserver(function () { classChanged(italicButton); });
italicObserver.observe(italicButton, { attributes: true, attributeFilter: ["class"] });
var underlineObserver = new MutationObserver(function () { classChanged(underlineButton); });
underlineObserver.observe(underlineButton, { attributes: true, attributeFilter: ["class"] });
var strikeObserver = new MutationObserver(function () { classChanged(strikeButton); });
strikeObserver.observe(strikeButton, { attributes: true, attributeFilter: ["class"] });
var orderedListObserver = new MutationObserver(function () { classAndValueChanged(orderedListButton); });
orderedListObserver.observe(orderedListButton, { attributes: true, attributeFilter: ["class"] });
var bulletListObserver = new MutationObserver(function () { classAndValueChanged(bulletListButton); });
bulletListObserver.observe(bulletListButton, { attributes: true, attributeFilter: ["class"] });
var directionObserver = new MutationObserver(function () { classChanged(directionButton); });
directionObserver.observe(directionButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserver = new MutationObserver(function () { alignmentDataValueChanged(alignLeftButton); });
alignmentObserver.observe(alignLeftButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserverCenter = new MutationObserver(function () { alignmentDataValueChanged(alignCenterButton); });
alignmentObserverCenter.observe(alignCenterButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserverRight = new MutationObserver(function () { alignmentDataValueChanged(alignRightButton); });
alignmentObserverRight.observe(alignRightButton, { attributes: true, attributeFilter: ["class"] });
var alignmentObserverJustify = new MutationObserver(function () { alignmentDataValueChanged(alignJustifyButton); });
alignmentObserverJustify.observe(alignJustifyButton, { attributes: true, attributeFilter: ["class"] });
function classChanged(button) {
window.chrome.webview.postMessage(`${button.className}`);
}
function classAndValueChanged(button) {
window.chrome.webview.postMessage(`${button.id} ${button.className}`);
}
function alignmentDataValueChanged(button) {
if (button.className.endsWith('ql-active'))
window.chrome.webview.postMessage(`${button.id}`);
}
function RenderHTML(htmlString) {
const delta = quill.clipboard.convert(htmlString)
quill.setContents(delta, 'silent')
}
function GetHTMLContent() {
return quill.root.innerHTML;
}
function GetTextContent() {
return quill.getText();
}
function SetLightEditor(){
DarkReader.disable();
}
function SetDarkEditor(){
DarkReader.enable();
}
// function ShowToolbar() {
// document.getElementById('editor-container').style.display = 'block';
// quill.focus();
// }
// function HideToolbar() {
// document.getElementById('editor-container').style.display = 'none';
// }
function getSelectedText() {
var range = quill.getSelection();
if (range) {
if (range.length == 0) {
}
else {
return quill.getText(range.index, range.length);
}
}
}
function addHyperlink(url) {
var range = quill.getSelection();
if (range) {
quill.formatText(range.index, range.length, 'link', url);
quill.setSelection(0, 0);
}
}
</script>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.hljs{display:block;overflow-x:auto;padding:0.5em;background:#23241f}.hljs,.hljs-tag,.hljs-subst{color:#f8f8f2}.hljs-strong,.hljs-emphasis{color:#a8a8a2}.hljs-bullet,.hljs-quote,.hljs-number,.hljs-regexp,.hljs-literal,.hljs-link{color:#ae81ff}.hljs-code,.hljs-title,.hljs-section,.hljs-selector-class{color:#a6e22e}.hljs-strong{font-weight:bold}.hljs-emphasis{font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-name,.hljs-attr{color:#f92672}.hljs-symbol,.hljs-attribute{color:#66d9ef}.hljs-params,.hljs-class .hljs-title{color:#f8f8f2}.hljs-string,.hljs-type,.hljs-built_in,.hljs-builtin-name,.hljs-selector-id,.hljs-selector-attr,.hljs-selector-pseudo,.hljs-addition,.hljs-variable,.hljs-template-variable{color:#e6db74}.hljs-comment,.hljs-deletion,.hljs-meta{color:#75715e}

View File

@@ -1,397 +0,0 @@
/*!
* Quill Editor v1.3.6
* https://quilljs.com/
* Copyright (c) 2014, Jason Chen
* Copyright (c) 2013, salesforce.com
*/
.ql-container {
box-sizing: border-box;
font-family: Helvetica, Arial, sans-serif;
font-size: 13px;
height: 100%;
margin: 0px;
position: relative;
}
.ql-container.ql-disabled .ql-tooltip {
visibility: hidden;
}
.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
pointer-events: none;
}
.ql-clipboard {
left: -100000px;
height: 1px;
overflow-y: hidden;
position: absolute;
top: 50%;
}
.ql-clipboard p {
margin: 0;
padding: 0;
}
.ql-editor {
box-sizing: border-box;
line-height: 1.42;
height: 100%;
outline: none;
overflow-y: auto;
padding: 12px 15px;
tab-size: 4;
-moz-tab-size: 4;
text-align: left;
white-space: pre-wrap;
word-wrap: break-word;
}
.ql-editor > * {
cursor: text;
}
.ql-editor p,
.ql-editor ol,
.ql-editor ul,
.ql-editor pre,
.ql-editor blockquote,
.ql-editor h1,
.ql-editor h2,
.ql-editor h3,
.ql-editor h4,
.ql-editor h5,
.ql-editor h6 {
margin: 0;
padding: 0;
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol,
.ql-editor ul {
padding-left: 1.5em;
}
.ql-editor ol > li,
.ql-editor ul > li {
list-style-type: none;
}
.ql-editor ul > li::before {
content: '\2022';
}
.ql-editor ul[data-checked=true],
.ql-editor ul[data-checked=false] {
pointer-events: none;
}
.ql-editor ul[data-checked=true] > li *,
.ql-editor ul[data-checked=false] > li * {
pointer-events: all;
}
.ql-editor ul[data-checked=true] > li::before,
.ql-editor ul[data-checked=false] > li::before {
color: #777;
cursor: pointer;
pointer-events: all;
}
.ql-editor ul[data-checked=true] > li::before {
content: '\2611';
}
.ql-editor ul[data-checked=false] > li::before {
content: '\2610';
}
.ql-editor li::before {
display: inline-block;
white-space: nowrap;
width: 1.2em;
}
.ql-editor li:not(.ql-direction-rtl)::before {
margin-left: -1.5em;
margin-right: 0.3em;
text-align: right;
}
.ql-editor li.ql-direction-rtl::before {
margin-left: 0.3em;
margin-right: -1.5em;
}
.ql-editor ol li:not(.ql-direction-rtl),
.ql-editor ul li:not(.ql-direction-rtl) {
padding-left: 1.5em;
}
.ql-editor ol li.ql-direction-rtl,
.ql-editor ul li.ql-direction-rtl {
padding-right: 1.5em;
}
.ql-editor ol li {
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
counter-increment: list-0;
}
.ql-editor ol li:before {
content: counter(list-0, decimal) '. ';
}
.ql-editor ol li.ql-indent-1 {
counter-increment: list-1;
}
.ql-editor ol li.ql-indent-1:before {
content: counter(list-1, lower-alpha) '. ';
}
.ql-editor ol li.ql-indent-1 {
counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-2 {
counter-increment: list-2;
}
.ql-editor ol li.ql-indent-2:before {
content: counter(list-2, lower-roman) '. ';
}
.ql-editor ol li.ql-indent-2 {
counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-3 {
counter-increment: list-3;
}
.ql-editor ol li.ql-indent-3:before {
content: counter(list-3, decimal) '. ';
}
.ql-editor ol li.ql-indent-3 {
counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-4 {
counter-increment: list-4;
}
.ql-editor ol li.ql-indent-4:before {
content: counter(list-4, lower-alpha) '. ';
}
.ql-editor ol li.ql-indent-4 {
counter-reset: list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-5 {
counter-increment: list-5;
}
.ql-editor ol li.ql-indent-5:before {
content: counter(list-5, lower-roman) '. ';
}
.ql-editor ol li.ql-indent-5 {
counter-reset: list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-6 {
counter-increment: list-6;
}
.ql-editor ol li.ql-indent-6:before {
content: counter(list-6, decimal) '. ';
}
.ql-editor ol li.ql-indent-6 {
counter-reset: list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-7 {
counter-increment: list-7;
}
.ql-editor ol li.ql-indent-7:before {
content: counter(list-7, lower-alpha) '. ';
}
.ql-editor ol li.ql-indent-7 {
counter-reset: list-8 list-9;
}
.ql-editor ol li.ql-indent-8 {
counter-increment: list-8;
}
.ql-editor ol li.ql-indent-8:before {
content: counter(list-8, lower-roman) '. ';
}
.ql-editor ol li.ql-indent-8 {
counter-reset: list-9;
}
.ql-editor ol li.ql-indent-9 {
counter-increment: list-9;
}
.ql-editor ol li.ql-indent-9:before {
content: counter(list-9, decimal) '. ';
}
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
padding-left: 3em;
}
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
padding-left: 4.5em;
}
.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
padding-right: 3em;
}
.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
padding-right: 4.5em;
}
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
padding-left: 6em;
}
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
padding-left: 7.5em;
}
.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
padding-right: 6em;
}
.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
padding-right: 7.5em;
}
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
padding-left: 9em;
}
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
padding-left: 10.5em;
}
.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
padding-right: 9em;
}
.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
padding-right: 10.5em;
}
.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
padding-left: 12em;
}
.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
padding-left: 13.5em;
}
.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
padding-right: 12em;
}
.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
padding-right: 13.5em;
}
.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
padding-left: 15em;
}
.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
padding-left: 16.5em;
}
.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
padding-right: 15em;
}
.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
padding-right: 16.5em;
}
.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
padding-left: 18em;
}
.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
padding-left: 19.5em;
}
.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
padding-right: 18em;
}
.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
padding-right: 19.5em;
}
.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
padding-left: 21em;
}
.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
padding-left: 22.5em;
}
.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
padding-right: 21em;
}
.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
padding-right: 22.5em;
}
.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
padding-left: 24em;
}
.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
padding-left: 25.5em;
}
.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
padding-right: 24em;
}
.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
padding-right: 25.5em;
}
.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
padding-left: 27em;
}
.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
padding-left: 28.5em;
}
.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
padding-right: 27em;
}
.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
padding-right: 28.5em;
}
.ql-editor .ql-video {
display: block;
max-width: 100%;
}
.ql-editor .ql-video.ql-align-center {
margin: 0 auto;
}
.ql-editor .ql-video.ql-align-right {
margin: 0 0 0 auto;
}
.ql-editor .ql-bg-black {
background-color: #000;
}
.ql-editor .ql-bg-red {
background-color: #e60000;
}
.ql-editor .ql-bg-orange {
background-color: #f90;
}
.ql-editor .ql-bg-yellow {
background-color: #ff0;
}
.ql-editor .ql-bg-green {
background-color: #008a00;
}
.ql-editor .ql-bg-blue {
background-color: #06c;
}
.ql-editor .ql-bg-purple {
background-color: #93f;
}
.ql-editor .ql-color-white {
color: #fff;
}
.ql-editor .ql-color-red {
color: #e60000;
}
.ql-editor .ql-color-orange {
color: #f90;
}
.ql-editor .ql-color-yellow {
color: #ff0;
}
.ql-editor .ql-color-green {
color: #008a00;
}
.ql-editor .ql-color-blue {
color: #06c;
}
.ql-editor .ql-color-purple {
color: #93f;
}
.ql-editor .ql-font-serif {
font-family: Georgia, Times New Roman, serif;
}
.ql-editor .ql-font-monospace {
font-family: Monaco, Courier New, monospace;
}
.ql-editor .ql-size-small {
font-size: 0.75em;
}
.ql-editor .ql-size-large {
font-size: 1.5em;
}
.ql-editor .ql-size-huge {
font-size: 2.5em;
}
.ql-editor .ql-direction-rtl {
direction: rtl;
text-align: inherit;
}
.ql-editor .ql-align-center {
text-align: center;
}
.ql-editor .ql-align-justify {
text-align: justify;
}
.ql-editor .ql-align-right {
text-align: right;
}
.ql-editor.ql-blank::before {
color: rgba(0,0,0,0.6);
content: attr(data-placeholder);
font-style: italic;
left: 15px;
pointer-events: none;
position: absolute;
right: 15px;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,947 +0,0 @@
/*!
* Quill Editor v1.3.6
* https://quilljs.com/
* Copyright (c) 2014, Jason Chen
* Copyright (c) 2013, salesforce.com
*/
.ql-container {
font-family: Helvetica, Arial, sans-serif;
font-size: 13px;
height: 1000px !important;
resize: vertical;
margin: 0px;
}
.ql-container.ql-disabled .ql-tooltip {
visibility: hidden;
}
.ql-container.ql-disabled .ql-editor ul[data-checked] > li::before {
pointer-events: none;
}
.ql-clipboard {
left: -100000px;
height: 1px;
overflow-y: hidden;
position: absolute;
top: 50%;
}
.ql-clipboard p {
margin: 0;
padding: 0;
}
.ql-editor {
line-height: 1.40;
height: 100%;
outline: none;
overflow-y: auto;
padding: 4px 4px;
tab-size: 4;
-moz-tab-size: 4;
text-align: left;
white-space: pre-wrap;
word-wrap: break-word;
}
/* Reply Border */
.ql-container.ql-snow {
/* border: solid #636e72; */
}
.ql-editor > * {
cursor: text;
}
.ql-editor p,
.ql-editor ol,
.ql-editor ul,
.ql-editor pre,
.ql-editor blockquote,
.ql-editor h1,
.ql-editor h2,
.ql-editor h3,
.ql-editor h4,
.ql-editor h5,
.ql-editor h6 {
margin: 0;
padding: 0;
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol,
.ql-editor ul {
padding-left: 1.5em;
}
.ql-editor ol > li,
.ql-editor ul > li {
list-style-type: none;
}
.ql-editor ul > li::before {
content: '\2022';
}
.ql-editor ul[data-checked=true],
.ql-editor ul[data-checked=false] {
pointer-events: none;
}
.ql-editor ul[data-checked=true] > li *,
.ql-editor ul[data-checked=false] > li * {
pointer-events: all;
}
.ql-editor ul[data-checked=true] > li::before,
.ql-editor ul[data-checked=false] > li::before {
color: #777;
cursor: pointer;
pointer-events: all;
}
.ql-editor ul[data-checked=true] > li::before {
content: '\2611';
}
.ql-editor ul[data-checked=false] > li::before {
content: '\2610';
}
.ql-editor li::before {
display: inline-block;
white-space: nowrap;
width: 1.2em;
}
.ql-editor li:not(.ql-direction-rtl)::before {
margin-left: -1.5em;
margin-right: 0.3em;
text-align: right;
}
.ql-editor li.ql-direction-rtl::before {
margin-left: 0.3em;
margin-right: -1.5em;
}
.ql-editor ol li:not(.ql-direction-rtl),
.ql-editor ul li:not(.ql-direction-rtl) {
padding-left: 1.5em;
}
.ql-editor ol li.ql-direction-rtl,
.ql-editor ul li.ql-direction-rtl {
padding-right: 1.5em;
}
.ql-editor ol li {
counter-reset: list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
counter-increment: list-0;
}
.ql-editor ol li:before {
content: counter(list-0, decimal) '. ';
}
.ql-editor ol li.ql-indent-1 {
counter-increment: list-1;
}
.ql-editor ol li.ql-indent-1:before {
content: counter(list-1, lower-alpha) '. ';
}
.ql-editor ol li.ql-indent-1 {
counter-reset: list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-2 {
counter-increment: list-2;
}
.ql-editor ol li.ql-indent-2:before {
content: counter(list-2, lower-roman) '. ';
}
.ql-editor ol li.ql-indent-2 {
counter-reset: list-3 list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-3 {
counter-increment: list-3;
}
.ql-editor ol li.ql-indent-3:before {
content: counter(list-3, decimal) '. ';
}
.ql-editor ol li.ql-indent-3 {
counter-reset: list-4 list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-4 {
counter-increment: list-4;
}
.ql-editor ol li.ql-indent-4:before {
content: counter(list-4, lower-alpha) '. ';
}
.ql-editor ol li.ql-indent-4 {
counter-reset: list-5 list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-5 {
counter-increment: list-5;
}
.ql-editor ol li.ql-indent-5:before {
content: counter(list-5, lower-roman) '. ';
}
.ql-editor ol li.ql-indent-5 {
counter-reset: list-6 list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-6 {
counter-increment: list-6;
}
.ql-editor ol li.ql-indent-6:before {
content: counter(list-6, decimal) '. ';
}
.ql-editor ol li.ql-indent-6 {
counter-reset: list-7 list-8 list-9;
}
.ql-editor ol li.ql-indent-7 {
counter-increment: list-7;
}
.ql-editor ol li.ql-indent-7:before {
content: counter(list-7, lower-alpha) '. ';
}
.ql-editor ol li.ql-indent-7 {
counter-reset: list-8 list-9;
}
.ql-editor ol li.ql-indent-8 {
counter-increment: list-8;
}
.ql-editor ol li.ql-indent-8:before {
content: counter(list-8, lower-roman) '. ';
}
.ql-editor ol li.ql-indent-8 {
counter-reset: list-9;
}
.ql-editor ol li.ql-indent-9 {
counter-increment: list-9;
}
.ql-editor ol li.ql-indent-9:before {
content: counter(list-9, decimal) '. ';
}
.ql-editor .ql-indent-1:not(.ql-direction-rtl) {
padding-left: 3em;
}
.ql-editor li.ql-indent-1:not(.ql-direction-rtl) {
padding-left: 4.5em;
}
.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right {
padding-right: 3em;
}
.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right {
padding-right: 4.5em;
}
.ql-editor .ql-indent-2:not(.ql-direction-rtl) {
padding-left: 6em;
}
.ql-editor li.ql-indent-2:not(.ql-direction-rtl) {
padding-left: 7.5em;
}
.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right {
padding-right: 6em;
}
.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right {
padding-right: 7.5em;
}
.ql-editor .ql-indent-3:not(.ql-direction-rtl) {
padding-left: 9em;
}
.ql-editor li.ql-indent-3:not(.ql-direction-rtl) {
padding-left: 10.5em;
}
.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right {
padding-right: 9em;
}
.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right {
padding-right: 10.5em;
}
.ql-editor .ql-indent-4:not(.ql-direction-rtl) {
padding-left: 12em;
}
.ql-editor li.ql-indent-4:not(.ql-direction-rtl) {
padding-left: 13.5em;
}
.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right {
padding-right: 12em;
}
.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right {
padding-right: 13.5em;
}
.ql-editor .ql-indent-5:not(.ql-direction-rtl) {
padding-left: 15em;
}
.ql-editor li.ql-indent-5:not(.ql-direction-rtl) {
padding-left: 16.5em;
}
.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right {
padding-right: 15em;
}
.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right {
padding-right: 16.5em;
}
.ql-editor .ql-indent-6:not(.ql-direction-rtl) {
padding-left: 18em;
}
.ql-editor li.ql-indent-6:not(.ql-direction-rtl) {
padding-left: 19.5em;
}
.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right {
padding-right: 18em;
}
.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right {
padding-right: 19.5em;
}
.ql-editor .ql-indent-7:not(.ql-direction-rtl) {
padding-left: 21em;
}
.ql-editor li.ql-indent-7:not(.ql-direction-rtl) {
padding-left: 22.5em;
}
.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right {
padding-right: 21em;
}
.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right {
padding-right: 22.5em;
}
.ql-editor .ql-indent-8:not(.ql-direction-rtl) {
padding-left: 24em;
}
.ql-editor li.ql-indent-8:not(.ql-direction-rtl) {
padding-left: 25.5em;
}
.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right {
padding-right: 24em;
}
.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right {
padding-right: 25.5em;
}
.ql-editor .ql-indent-9:not(.ql-direction-rtl) {
padding-left: 27em;
}
.ql-editor li.ql-indent-9:not(.ql-direction-rtl) {
padding-left: 28.5em;
}
.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right {
padding-right: 27em;
}
.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right {
padding-right: 28.5em;
}
.ql-editor .ql-video {
display: block;
max-width: 100%;
}
.ql-editor .ql-video.ql-align-center {
margin: 0 auto;
}
.ql-editor .ql-video.ql-align-right {
margin: 0 0 0 auto;
}
.ql-editor .ql-bg-black {
background-color: #000;
}
.ql-editor .ql-bg-red {
background-color: #e60000;
}
.ql-editor .ql-bg-orange {
background-color: #f90;
}
.ql-editor .ql-bg-yellow {
background-color: #ff0;
}
.ql-editor .ql-bg-green {
background-color: #008a00;
}
.ql-editor .ql-bg-blue {
background-color: #06c;
}
.ql-editor .ql-bg-purple {
background-color: #93f;
}
.ql-editor .ql-color-white {
color: #fff;
}
.ql-editor .ql-color-red {
color: #e60000;
}
.ql-editor .ql-color-orange {
color: #f90;
}
.ql-editor .ql-color-yellow {
color: #ff0;
}
.ql-editor .ql-color-green {
color: #008a00;
}
.ql-editor .ql-color-blue {
color: #06c;
}
.ql-editor .ql-color-purple {
color: #93f;
}
.ql-editor .ql-font-serif {
font-family: Georgia, Times New Roman, serif;
}
.ql-editor .ql-font-monospace {
font-family: Monaco, Courier New, monospace;
}
.ql-editor .ql-size-small {
font-size: 0.75em;
}
.ql-editor .ql-size-large {
font-size: 1.5em;
}
.ql-editor .ql-size-huge {
font-size: 2.5em;
}
.ql-editor .ql-direction-rtl {
direction: rtl;
text-align: inherit;
}
.ql-editor .ql-align-center {
text-align: center;
}
.ql-editor .ql-align-justify {
text-align: justify;
}
.ql-editor .ql-align-right {
text-align: right;
}
.ql-editor.ql-blank::before {
color: rgba(0,0,0,0.6);
content: attr(data-placeholder);
left: 4px;
pointer-events: none;
position: absolute;
right: 4px;
}
.ql-snow.ql-toolbar:after,
.ql-snow .ql-toolbar:after {
clear: both;
content: '';
display: table;
}
.ql-snow.ql-toolbar button,
.ql-snow .ql-toolbar button {
background: none;
border: none;
cursor: pointer;
display: inline-block;
float: left;
height: 24px;
padding: 3px 5px;
width: 28px;
}
.ql-snow.ql-toolbar button svg,
.ql-snow .ql-toolbar button svg {
float: left;
height: 100%;
}
.ql-snow.ql-toolbar button:active:hover,
.ql-snow .ql-toolbar button:active:hover {
outline: none;
}
.ql-snow.ql-toolbar input.ql-image[type=file],
.ql-snow .ql-toolbar input.ql-image[type=file] {
display: none;
}
.ql-snow.ql-toolbar button:hover,
.ql-snow .ql-toolbar button:hover,
.ql-snow.ql-toolbar button:focus,
.ql-snow .ql-toolbar button:focus,
.ql-snow.ql-toolbar button.ql-active,
.ql-snow .ql-toolbar button.ql-active,
.ql-snow.ql-toolbar .ql-picker-label:hover,
.ql-snow .ql-toolbar .ql-picker-label:hover,
.ql-snow.ql-toolbar .ql-picker-label.ql-active,
.ql-snow .ql-toolbar .ql-picker-label.ql-active,
.ql-snow.ql-toolbar .ql-picker-item:hover,
.ql-snow .ql-toolbar .ql-picker-item:hover,
.ql-snow.ql-toolbar .ql-picker-item.ql-selected,
.ql-snow .ql-toolbar .ql-picker-item.ql-selected {
color: #06c;
}
.ql-snow.ql-toolbar button:hover .ql-fill,
.ql-snow .ql-toolbar button:hover .ql-fill,
.ql-snow.ql-toolbar button:focus .ql-fill,
.ql-snow .ql-toolbar button:focus .ql-fill,
.ql-snow.ql-toolbar button.ql-active .ql-fill,
.ql-snow .ql-toolbar button.ql-active .ql-fill,
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,
.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,
.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,
.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,
.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,
.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,
.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill {
fill: #06c;
}
.ql-snow.ql-toolbar button:hover .ql-stroke,
.ql-snow .ql-toolbar button:hover .ql-stroke,
.ql-snow.ql-toolbar button:focus .ql-stroke,
.ql-snow .ql-toolbar button:focus .ql-stroke,
.ql-snow.ql-toolbar button.ql-active .ql-stroke,
.ql-snow .ql-toolbar button.ql-active .ql-stroke,
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,
.ql-snow.ql-toolbar button:hover .ql-stroke-miter,
.ql-snow .ql-toolbar button:hover .ql-stroke-miter,
.ql-snow.ql-toolbar button:focus .ql-stroke-miter,
.ql-snow .ql-toolbar button:focus .ql-stroke-miter,
.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,
.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,
.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,
.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,
.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,
.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,
.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter {
stroke: #06c;
}
@media (pointer: coarse) {
.ql-snow.ql-toolbar button:hover:not(.ql-active),
.ql-snow .ql-toolbar button:hover:not(.ql-active) {
color: #444;
}
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill {
fill: #444;
}
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,
.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,
.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter {
stroke: #444;
}
}
.ql-snow {
box-sizing: border-box;
}
.ql-snow * {
box-sizing: border-box;
}
.ql-snow .ql-hidden {
display: none;
}
.ql-snow .ql-out-bottom,
.ql-snow .ql-out-top {
visibility: hidden;
}
.ql-snow .ql-tooltip {
position: absolute;
transform: translateY(10px);
}
.ql-snow .ql-tooltip a {
cursor: pointer;
text-decoration: none;
}
.ql-snow .ql-tooltip.ql-flip {
transform: translateY(-10px);
}
.ql-snow .ql-formats {
display: inline-block;
vertical-align: middle;
}
.ql-snow .ql-formats:after {
clear: both;
content: '';
display: table;
}
.ql-snow .ql-stroke {
fill: none;
stroke: #444;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 2;
}
.ql-snow .ql-stroke-miter {
fill: none;
stroke: #444;
stroke-miterlimit: 10;
stroke-width: 2;
}
.ql-snow .ql-fill,
.ql-snow .ql-stroke.ql-fill {
fill: #444;
}
.ql-snow .ql-empty {
fill: none;
}
.ql-snow .ql-even {
fill-rule: evenodd;
}
.ql-snow .ql-thin,
.ql-snow .ql-stroke.ql-thin {
stroke-width: 1;
}
.ql-snow .ql-transparent {
opacity: 0.4;
}
.ql-snow .ql-direction svg:last-child {
display: none;
}
.ql-snow .ql-direction.ql-active svg:last-child {
display: inline;
}
.ql-snow .ql-direction.ql-active svg:first-child {
display: none;
}
.ql-snow .ql-editor h1 {
font-size: 2em;
}
.ql-snow .ql-editor h2 {
font-size: 1.5em;
}
.ql-snow .ql-editor h3 {
font-size: 1.17em;
}
.ql-snow .ql-editor h4 {
font-size: 1em;
}
.ql-snow .ql-editor h5 {
font-size: 0.83em;
}
.ql-snow .ql-editor h6 {
font-size: 0.67em;
}
.ql-snow .ql-editor a {
text-decoration: underline;
}
.ql-snow .ql-editor blockquote {
border-left: 4px solid #ccc;
margin-bottom: 5px;
margin-top: 5px;
padding-left: 16px;
}
.ql-snow .ql-editor code,
.ql-snow .ql-editor pre {
background-color: #f0f0f0;
border-radius: 3px;
}
.ql-snow .ql-editor pre {
white-space: pre-wrap;
margin-bottom: 5px;
margin-top: 5px;
padding: 5px 10px;
}
.ql-snow .ql-editor code {
font-size: 85%;
padding: 2px 4px;
}
.ql-snow .ql-editor pre.ql-syntax {
background-color: #23241f;
color: #f8f8f2;
overflow: visible;
}
.ql-snow .ql-editor img {
max-width: 100%;
}
.ql-snow .ql-picker {
color: #444;
display: inline-block;
float: left;
font-size: 14px;
font-weight: 500;
height: 24px;
position: relative;
vertical-align: middle;
}
.ql-snow .ql-picker-label {
cursor: pointer;
display: inline-block;
height: 100%;
padding-left: 8px;
padding-right: 2px;
position: relative;
width: 100%;
}
.ql-snow .ql-picker-label::before {
display: inline-block;
line-height: 22px;
}
.ql-snow .ql-picker-options {
background-color: #fff;
display: none;
min-width: 100%;
padding: 4px 8px;
position: absolute;
white-space: nowrap;
}
.ql-snow .ql-picker-options .ql-picker-item {
cursor: pointer;
display: block;
padding-bottom: 5px;
padding-top: 5px;
}
.ql-snow .ql-picker.ql-expanded .ql-picker-label {
color: #ccc;
z-index: 2;
}
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill {
fill: #ccc;
}
.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke {
stroke: #ccc;
}
.ql-snow .ql-picker.ql-expanded .ql-picker-options {
display: block;
margin-top: -1px;
top: 100%;
z-index: 1;
}
.ql-snow .ql-color-picker,
.ql-snow .ql-icon-picker {
width: 28px;
}
.ql-snow .ql-color-picker .ql-picker-label,
.ql-snow .ql-icon-picker .ql-picker-label {
padding: 2px 4px;
}
.ql-snow .ql-color-picker .ql-picker-label svg,
.ql-snow .ql-icon-picker .ql-picker-label svg {
right: 4px;
}
.ql-snow .ql-icon-picker .ql-picker-options {
padding: 4px 0px;
}
.ql-snow .ql-icon-picker .ql-picker-item {
height: 24px;
width: 24px;
padding: 2px 4px;
}
.ql-snow .ql-color-picker .ql-picker-options {
padding: 3px 5px;
width: 152px;
}
.ql-snow .ql-color-picker .ql-picker-item {
border: 1px solid transparent;
float: left;
height: 16px;
margin: 2px;
padding: 0px;
width: 16px;
}
.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg {
position: absolute;
margin-top: -9px;
right: 0;
top: 50%;
width: 18px;
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,
.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,
.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before {
content: attr(data-label);
}
.ql-snow .ql-picker.ql-header {
width: 98px;
}
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
content: 'Normal';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
content: 'Heading 1';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
content: 'Heading 2';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
content: 'Heading 3';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
content: 'Heading 4';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
content: 'Heading 5';
}
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
content: 'Heading 6';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
font-size: 2em;
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
font-size: 1.5em;
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
font-size: 1.17em;
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
font-size: 1em;
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
font-size: 0.83em;
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
font-size: 0.67em;
}
.ql-snow .ql-picker.ql-font {
width: 108px;
}
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
content: 'Sans Serif';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
content: 'Serif';
}
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
content: 'Monospace';
}
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before {
font-family: Georgia, Times New Roman, serif;
}
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before {
font-family: Monaco, Courier New, monospace;
}
.ql-snow .ql-picker.ql-size {
width: 98px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
content: 'Normal';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
content: 'Small';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
content: 'Large';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
content: 'Huge';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before {
font-size: 10px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before {
font-size: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before {
font-size: 32px;
}
.ql-snow .ql-color-picker.ql-background .ql-picker-item {
background-color: #fff;
}
.ql-snow .ql-color-picker.ql-color .ql-picker-item {
background-color: #000;
}
.ql-toolbar.ql-snow {
border: 1px solid #ccc;
box-sizing: border-box;
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
padding: 8px;
}
.ql-toolbar.ql-snow .ql-formats {
margin-right: 15px;
}
.ql-toolbar.ql-snow .ql-picker-label {
border: 1px solid transparent;
}
.ql-toolbar.ql-snow .ql-picker-options {
border: 1px solid transparent;
box-shadow: rgba(0,0,0,0.2) 0 2px 8px;
}
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label {
border-color: #ccc;
}
.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options {
border-color: #ccc;
}
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,
.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover {
border-color: #000;
}
.ql-toolbar.ql-snow + .ql-container.ql-snow {
border-top: 0px;
}
.ql-snow .ql-tooltip {
background-color: #fff;
border: 1px solid #ccc;
box-shadow: 0px 0px 5px #ddd;
color: #444;
padding: 5px 12px;
white-space: nowrap;
}
.ql-snow .ql-tooltip::before {
content: "Visit URL:";
line-height: 26px;
margin-right: 8px;
}
.ql-snow .ql-tooltip input[type=text] {
display: none;
border: 1px solid #ccc;
font-size: 13px;
height: 26px;
margin: 0px;
padding: 3px 5px;
width: 170px;
}
.ql-snow .ql-tooltip a.ql-preview {
display: inline-block;
max-width: 200px;
overflow-x: hidden;
text-overflow: ellipsis;
vertical-align: top;
}
.ql-snow .ql-tooltip a.ql-action::after {
border-right: 1px solid #ccc;
content: 'Edit';
margin-left: 16px;
padding-right: 8px;
}
.ql-snow .ql-tooltip a.ql-remove::before {
content: 'Remove';
margin-left: 8px;
}
.ql-snow .ql-tooltip a {
line-height: 26px;
}
.ql-snow .ql-tooltip.ql-editing a.ql-preview,
.ql-snow .ql-tooltip.ql-editing a.ql-remove {
display: none;
}
.ql-snow .ql-tooltip.ql-editing input[type=text] {
display: inline-block;
}
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
border-right: 0px;
content: 'Save';
padding-right: 0px;
}
.ql-snow .ql-tooltip[data-mode=link]::before {
content: "Enter link:";
}
.ql-snow .ql-tooltip[data-mode=formula]::before {
content: "Enter formula:";
}
.ql-snow .ql-tooltip[data-mode=video]::before {
content: "Enter video:";
}
.ql-snow a {
color: #06c;
}

View File

@@ -1,10 +0,0 @@
#readerDiv {
font-family: Arial, Helvetica, sans-serif;
background-color: transparent !important;
}
/*@font-face {
font-family: Starborn;
src: url(Fonts/Starborn.ttf);
}
*/

51
Wino.Mail/JS/editor.html Normal file
View File

@@ -0,0 +1,51 @@
<!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;
}
#editor {
height: 100%;
}
.jodit-container:not(.jodit_inline) {
background-color: transparent;
border: none;
border-radius: initial;
}
.jodit-wysiwyg {
min-height: 200px
}
/* 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;
}
body {
margin: 8px;
}
</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>

104
Wino.Mail/JS/editor.js Normal file
View File

@@ -0,0 +1,104 @@
const editor = Jodit.make("#editor", {
"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,
"disablePlugins": "add-new-line",
"showPlaceholder": false,
"uploader": {
"insertImageAsBase64URI": true
},
"enter": "DIV",
"minHeight": 200
});
// 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([base64Image]);
};
reader.readAsDataURL(file);
}
});
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');
console.log(buttonContainer, ref, value);
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 == 'true') {
toolbar.style.display = 'flex';
}
else {
toolbar.style.display = 'none';
}
}
function insertImages(images) {
images.forEach(image => {
editor.selection.insertHTML(`<img src="${image}" alt="Embedded Image">`);
});
};

5664
Wino.Mail/JS/libs/jodit.min.css vendored Normal file

File diff suppressed because one or more lines are too long

10
Wino.Mail/JS/libs/jodit.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,25 @@
<html>
<body style="padding-left:12px; padding-right:12px; padding-top:8px; padding-bottom: 8px; margin: 0px; border-radius: 8px;">
<meta name="color-scheme" content="dark light">
<script src="https://app.reader/darkreader.js"></script>
<head>
<link rel="stylesheet" href="./global.css" />
<script src="./libs/darkreader.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;
}
</style>
</head>
<body>
<meta name="color-scheme" content="dark light">
<script>
function RenderHTML(htmlString) {
var containerDiv = document.getElementById("readerDiv");
@@ -29,10 +46,6 @@
DarkReader.enable();
}
</script>
<link rel="stylesheet" href="https://app.reader/reader.css" />
<div id="readerDiv">
</div>
<link rel="stylesheet" href="https://app.reader/global.css" />
<div id="readerDiv"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -151,13 +151,25 @@
OverflowButtonVisibility="Collapsed">
<CommandBar.PrimaryCommands>
<AppBarButton
x:Name="ComposerThemeToggleButton"
Click="InvertComposerThemeClicked"
LabelPosition="Collapsed">
LabelPosition="Collapsed"
ToolTipService.ToolTip="Light Theme"
Visibility="{x:Bind IsComposerDarkMode, Mode=OneWay}">
<AppBarButton.Icon>
<controls:WinoFontIcon Icon="LightEditor" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton
Click="InvertComposerThemeClicked"
LabelPosition="Collapsed"
ToolTipService.ToolTip="Dark Theme"
Visibility="{x:Bind IsComposerDarkMode, Mode=OneWay, Converter={StaticResource ReverseBooleanToVisibilityConverter}}">
<AppBarButton.Icon>
<controls:WinoFontIcon Icon="DarkEditor" />
</AppBarButton.Icon>
</AppBarButton>
<AppBarButton Command="{x:Bind ViewModel.DiscardCommand}" Label="Discard">
<AppBarButton.Icon>
<controls:WinoFontIcon Icon="Delete" />
@@ -186,281 +198,220 @@
</Grid>
<!-- Editor Options -->
<ScrollViewer
Grid.Row="1"
Margin="0,6"
VerticalAlignment="Top"
HorizontalScrollBarVisibility="Hidden"
HorizontalScrollMode="Enabled"
VerticalScrollBarVisibility="Hidden"
VerticalScrollMode="Disabled">
<Grid Margin="16,0,16,20" Padding="8,0">
<!-- Format Panel -->
<Grid x:Name="FormatPanel" Visibility="{x:Bind helpers:XamlHelpers.IsFormatSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="12">
<ToggleButton
x:Name="BoldButton"
Height="32"
Click="BoldButtonClicked">
<ToggleButton.Content>
<Grid Grid.Row="1" Padding="26,0,6,6">
<CommandBar
HorizontalAlignment="Left"
DefaultLabelPosition="Collapsed"
IsDynamicOverflowEnabled="True"
Visibility="{x:Bind helpers:XamlHelpers.IsFormatSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<AppBarToggleButton
x:Name="BoldButton"
Click="BoldButtonClicked"
Label="Bold">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource BoldPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton
x:Name="ItalicButton"
Click="ItalicButtonClicked"
Label="Italic">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource ItalicPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton
x:Name="UnderlineButton"
Click="UnderlineButtonClicked"
Label="Underline">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource UnderlinePathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton
x:Name="StrokeButton"
Click="StrokeButtonClicked"
Label="Stroke">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource StrikePathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarSeparator />
<AppBarToggleButton
x:Name="BulletListButton"
Click="BulletListButtonClicked"
Label="Bullet List">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource BulletedListPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton
x:Name="OrderedListButton"
Click="OrderedListButtonClicked"
Label="Ordered List">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource OrderedListPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarSeparator />
<AppBarButton
x:Name="DecreaseIndentButton"
Click="DecreaseIndentClicked"
Label="Outdent">
<AppBarButton.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource DecreaseIndentPathIcon}" />
</Viewbox>
</AppBarButton.Content>
</AppBarButton>
<AppBarButton
x:Name="IncreaseIndentButton"
Click="IncreaseIndentClicked"
Label="Indent">
<AppBarButton.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource IncreaseIndentPathIcon}" />
</Viewbox>
</AppBarButton.Content>
</AppBarButton>
<AppBarElementContainer HorizontalAlignment="Center" VerticalAlignment="Center">
<ComboBox
x:Name="AlignmentListView"
VerticalAlignment="Center"
Background="Transparent"
BorderBrush="Transparent"
SelectionChanged="AlignmentChanged">
<ComboBoxItem IsSelected="True" Tag="left">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource BoldPathIcon}" />
<PathIcon Data="{StaticResource AlignLeftPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<ToggleButton
x:Name="ItalicButton"
Height="32"
Click="ItalicButtonClicked">
<ToggleButton.Content>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Left}" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem Tag="center">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource ItalicPathIcon}" />
<PathIcon Data="{StaticResource AlignCenterPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<ToggleButton
x:Name="UnderlineButton"
Height="32"
Click="UnderlineButtonClicked">
<ToggleButton.Content>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Center}" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem Tag="right">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource UnderlinePathIcon}" />
<PathIcon Data="{StaticResource AlignRightPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<ToggleButton
x:Name="StrokeButton"
Height="32"
Click="StrokeButtonClicked">
<ToggleButton.Content>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Right}" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem Tag="justify">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource StrikePathIcon}" />
<PathIcon Data="{StaticResource AlignJustifyPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Justify}" />
</StackPanel>
</ComboBoxItem>
</ComboBox>
</AppBarElementContainer>
<AppBarSeparator />
<AppBarSeparator />
<ToggleButton
x:Name="BulletListButton"
Height="32"
Click="BulletListButtonClicked">
<ToggleButton.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource BulletedListPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<AppBarToggleButton
x:Name="WebviewToolBarButton"
Click="WebViewToggleButtonClicked"
Label="Webview ToolBar"
ToolTipService.ToolTip="Webview ToolBar">
<AppBarToggleButton.Icon>
<PathIcon Data="{StaticResource WebviewToolBarPathIcon}" />
</AppBarToggleButton.Icon>
</AppBarToggleButton>
</CommandBar>
<ToggleButton
x:Name="OrderedListButton"
Height="32"
Click="OrderedListButtonClicked">
<ToggleButton.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource OrderedListPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<!-- Insert Panel -->
<CommandBar
HorizontalAlignment="Left"
DefaultLabelPosition="Right"
IsDynamicOverflowEnabled="True"
Visibility="{x:Bind helpers:XamlHelpers.IsInsertSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<AppBarButton
x:Name="FilesButton"
Click="AddFilesClicked"
Label="{x:Bind domain:Translator.Files}">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource AttachPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<Button
x:Name="DecreaseIndentButton"
Height="32"
Click="DecreaseIndentClicked">
<Button.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource DecreaseIndentPathIcon}" />
</Viewbox>
</Button.Content>
</Button>
<AppBarButton
x:Name="AddImageButton"
Click="AddImageClicked"
Label="{x:Bind domain:Translator.Photos}">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource AddPhotoPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
<Button
x:Name="IncreaseIndentButton"
Height="32"
Click="IncreaseIndentClicked">
<Button.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource IncreaseIndentPathIcon}" />
</Viewbox>
</Button.Content>
</Button>
<AppBarButton
x:Name="EmojiButton"
Click="EmojiButtonClicked"
Label="{x:Bind domain:Translator.Emoji}">
<AppBarButton.Icon>
<PathIcon Data="{StaticResource EmojiPathIcon}" />
</AppBarButton.Icon>
</AppBarButton>
</CommandBar>
<AppBarSeparator />
<ToggleButton
x:Name="DirectionButton"
Height="32"
Click="DirectionButtonClicked">
<ToggleButton.Content>
<Viewbox Width="16">
<PathIcon Data="{StaticResource ParagraphPathIcon}" />
</Viewbox>
</ToggleButton.Content>
</ToggleButton>
<ComboBox
x:Name="AlignmentListView"
Width="120"
Height="32"
VerticalAlignment="Center"
SelectionChanged="AlignmentChanged">
<ComboBoxItem Tag="left">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource AlignLeftPathIcon}" />
</Viewbox>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Left}" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem Tag="center">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource AlignCenterPathIcon}" />
</Viewbox>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Center}" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem Tag="right">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource AlignRightPathIcon}" />
</Viewbox>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Right}" />
</StackPanel>
</ComboBoxItem>
<ComboBoxItem Tag="justify">
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16">
<PathIcon Data="{StaticResource AlignJustifyPathIcon}" />
</Viewbox>
<TextBlock VerticalAlignment="Center" Text="{x:Bind domain:Translator.Justify}" />
</StackPanel>
</ComboBoxItem>
</ComboBox>
</StackPanel>
</Grid>
<!-- Insert Panel -->
<Grid x:Name="InsertPanel" Visibility="{x:Bind helpers:XamlHelpers.IsInsertSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="12">
<Button
x:Name="FilesButton"
Height="32"
Click="AddFilesClicked">
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16" VerticalAlignment="Center">
<PathIcon Data="{StaticResource AttachPathIcon}" />
</Viewbox>
<TextBlock Text="{x:Bind domain:Translator.Files}" />
</StackPanel>
</Button.Content>
</Button>
<Button
x:Name="AddImageButton"
Height="32"
Click="AddImageClicked">
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16" VerticalAlignment="Center">
<PathIcon Data="{StaticResource AddPhotoPathIcon}" />
</Viewbox>
<TextBlock Text="{x:Bind domain:Translator.Photos}" />
</StackPanel>
</Button.Content>
</Button>
<Button
x:Name="EmojiButton"
Height="32"
Click="EmojiButtonClicked">
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16" VerticalAlignment="Center">
<PathIcon Data="{StaticResource EmojiPathIcon}" />
</Viewbox>
<TextBlock Text="{x:Bind domain:Translator.Emoji}" />
</StackPanel>
</Button.Content>
</Button>
<Button
x:Name="LinkButton"
Height="32"
Click="LinkButtonClicked">
<Button.Flyout>
<Flyout x:Name="HyperlinkFlyout">
<StackPanel Width="250" Spacing="6">
<TextBox x:Name="HyperlinkTextBox" Header="Text" />
<TextBox
x:Name="LinkUrlTextBox"
Header="Link"
PlaceholderText="https://" />
<Button
x:Name="AddHyperlinkButton"
Margin="0,6"
HorizontalAlignment="Stretch"
Click="HyperlinkAddClicked"
Content="{x:Bind domain:Translator.AddHyperlink}" />
</StackPanel>
</Flyout>
</Button.Flyout>
<Button.Content>
<StackPanel Orientation="Horizontal" Spacing="8">
<Viewbox Width="16" VerticalAlignment="Center">
<PathIcon Data="{StaticResource AddLinkPathIcon}" />
</Viewbox>
<TextBlock Text="Link" />
</StackPanel>
</Button.Content>
</Button>
</StackPanel>
</Grid>
<!-- Draw Panel -->
<Grid x:Name="DrawPanel" Visibility="{x:Bind helpers:XamlHelpers.IsDrawSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<TextBlock Text="{x:Bind domain:Translator.ComingSoon}" />
</Grid>
<!-- Options Panel -->
<Grid x:Name="OptionsPanel" Visibility="{x:Bind helpers:XamlHelpers.IsOptionsSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<muxc:ToggleSplitButton x:Name="ImportanceSplitButton" IsChecked="{x:Bind ViewModel.IsImportanceSelected, Mode=TwoWay}">
<SymbolIcon x:Name="ImportanceSplitButtonContent" Symbol="Important" />
<muxc:ToggleSplitButton.Flyout>
<Flyout x:Name="ImportanceFlyout" Placement="Bottom">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Padding" Value="4" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Margin" Value="6" />
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
</Style>
</StackPanel.Resources>
<Button Click="ImportanceClicked">
<Button.Tag>
<mailkit:MessageImportance>High</mailkit:MessageImportance>
</Button.Tag>
<SymbolIcon Symbol="Important" />
</Button>
<Button Click="ImportanceClicked">
<Button.Tag>
<mailkit:MessageImportance>Low</mailkit:MessageImportance>
</Button.Tag>
<SymbolIcon Symbol="Download" />
</Button>
</StackPanel>
</Flyout>
</muxc:ToggleSplitButton.Flyout>
</muxc:ToggleSplitButton>
</Grid>
<!-- Draw Panel -->
<Grid x:Name="DrawPanel" Visibility="{x:Bind helpers:XamlHelpers.IsDrawSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<TextBlock Text="{x:Bind domain:Translator.ComingSoon}" />
</Grid>
</ScrollViewer>
<!-- Options Panel -->
<Grid x:Name="OptionsPanel" Visibility="{x:Bind helpers:XamlHelpers.IsOptionsSection(ViewModel.SelectedToolbarSection), Mode=OneWay}">
<muxc:ToggleSplitButton x:Name="ImportanceSplitButton" IsChecked="{x:Bind ViewModel.IsImportanceSelected, Mode=TwoWay}">
<SymbolIcon x:Name="ImportanceSplitButtonContent" Symbol="Important" />
<muxc:ToggleSplitButton.Flyout>
<Flyout x:Name="ImportanceFlyout" Placement="Bottom">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Padding" Value="4" />
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Margin" Value="6" />
<Setter Property="CornerRadius" Value="{StaticResource ControlCornerRadius}" />
</Style>
</StackPanel.Resources>
<Button Click="ImportanceClicked">
<Button.Tag>
<mailkit:MessageImportance>High</mailkit:MessageImportance>
</Button.Tag>
<SymbolIcon Symbol="Important" />
</Button>
<Button Click="ImportanceClicked">
<Button.Tag>
<mailkit:MessageImportance>Low</mailkit:MessageImportance>
</Button.Tag>
<SymbolIcon Symbol="Download" />
</Button>
</StackPanel>
</Flyout>
</muxc:ToggleSplitButton.Flyout>
</muxc:ToggleSplitButton>
</Grid>
</Grid>
<!-- Mime Info -->
<Grid
@@ -644,7 +595,7 @@
Visibility="{x:Bind ViewModel.IsDraggingOverComposerGrid, Mode=OneWay}">
<Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}" CornerRadius="9">
<Rectangle
x:Name="DropZoneBorder"
x:Name="FilesDropZoneBorder"
Fill="Transparent"
Opacity="0.5"
RadiusX="9"
@@ -653,7 +604,7 @@
StrokeDashArray="3,4"
StrokeThickness="2" />
<TextBlock
x:Name="DropZoneText"
x:Name="FilesDropZoneText"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
@@ -662,38 +613,6 @@
Text="{x:Bind domain:Translator.ComposerAttachmentsDropZone_Message}" />
</Grid>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ComposerThemeToggleButtonStates">
<VisualState x:Name="DarkMode">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind IsComposerDarkMode, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ComposerThemeToggleButton.Label" Value="Light Reader" />
<Setter Target="ComposerThemeToggleButton.Icon">
<Setter.Value>
<controls:WinoFontIcon Icon="LightEditor" />
</Setter.Value>
</Setter>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="LightMode" />
</VisualStateGroup>
<VisualStateGroup x:Name="DropZoneState">
<VisualState x:Name="Hovered">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.IsDraggingOverDropZone, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="DropZoneText.Opacity" Value="1" />
<Setter Target="DropZoneBorder.Opacity" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="NotHovered" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</Border>
<Border
@@ -701,13 +620,67 @@
Background="{ThemeResource WinoContentZoneBackgroud}"
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
CornerRadius="10">
<Grid>
CornerRadius="7">
<Grid Margin="1" CornerRadius="7">
<Grid Background="White" Visibility="{x:Bind IsComposerDarkMode, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}" />
<muxc:WebView2 x:Name="Chromium" />
<!-- Dropzone for images -->
<Grid
AllowDrop="True"
DragEnter="OnImageDropGridDragEnter"
DragLeave="OnImageDropGridDragLeave"
Drop="OnImageDropGridImageDropped"
Visibility="{x:Bind ViewModel.IsDraggingOverComposerGrid, Mode=OneWay}">
<Grid Background="{ThemeResource AcrylicInAppFillColorDefaultBrush}" CornerRadius="9">
<Rectangle
x:Name="ImagesDropZoneBorder"
Fill="Transparent"
Opacity="0.5"
RadiusX="9"
RadiusY="9"
Stroke="{ThemeResource TextFillColorPrimaryBrush}"
StrokeDashArray="3,4"
StrokeThickness="2" />
<TextBlock
x:Name="ImagesDropZoneText"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="20"
FontWeight="SemiBold"
Opacity="0.5"
Text="{x:Bind domain:Translator.ComposerImagesDropZone_Message}" />
</Grid>
</Grid>
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FilesDropZoneState">
<VisualState x:Name="FilesDropZoneHovered">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.IsDraggingOverFilesDropZone, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="FilesDropZoneText.Opacity" Value="1" />
<Setter Target="FilesDropZoneBorder.Opacity" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="FilesDropZoneNotHovered" />
</VisualStateGroup>
<VisualStateGroup x:Name="ImagesDropZoneState">
<VisualState x:Name="ImagesDropZoneHovered">
<VisualState.StateTriggers>
<StateTrigger IsActive="{x:Bind ViewModel.IsDraggingOverImagesDropZone, Mode=OneWay}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="ImagesDropZoneText.Opacity" Value="1" />
<Setter Target="ImagesDropZoneBorder.Opacity" Value="1" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="ImagesDropZoneNotHovered" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>

View File

@@ -9,6 +9,7 @@ using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI.Controls;
using EmailValidation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Toolkit.Uwp.Helpers;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using MimeKit;
@@ -26,6 +27,7 @@ using Wino.Core.Domain;
using Wino.Core.Domain.Entities;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.Models.Requests;
using Wino.Core.Messages.Mails;
using Wino.Core.Messages.Shell;
using Wino.Extensions;
@@ -121,7 +123,7 @@ namespace Wino.Views
private void OnFileDropGridDragOver(object sender, DragEventArgs e)
{
ViewModel.IsDraggingOverDropZone = true;
ViewModel.IsDraggingOverFilesDropZone = true;
e.AcceptedOperation = DataPackageOperation.Copy;
e.DragUIOverride.Caption = Translator.ComposerAttachmentsDragDropAttach_Message;
@@ -132,21 +134,94 @@ namespace Wino.Views
private void OnFileDropGridDragLeave(object sender, DragEventArgs e)
{
ViewModel.IsDraggingOverDropZone = false;
ViewModel.IsDraggingOverFilesDropZone = false;
}
private async void OnFileDropGridFileDropped(object sender, DragEventArgs e)
{
try
{
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await e.DataView.GetStorageItemsAsync();
var files = storageItems.OfType<StorageFile>();
await AttachFiles(files);
}
}
// State should be reset even when an exception occurs, otherwise the UI will be stuck in a dragging state.
finally
{
ViewModel.IsDraggingOverComposerGrid = false;
ViewModel.IsDraggingOverFilesDropZone = false;
}
}
private void OnImageDropGridDragEnter(object sender, DragEventArgs e)
{
bool isValid = false;
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await e.DataView.GetStorageItemsAsync();
var files = storageItems.OfType<StorageFile>();
// We can't use async/await here because DragUIOverride becomes inaccessible.
// https://github.com/microsoft/microsoft-ui-xaml/issues/9296
var files = e.DataView.GetStorageItemsAsync().GetAwaiter().GetResult().OfType<StorageFile>();
await AttachFiles(files);
foreach (var file in files)
{
if (ValidateImageFile(file))
{
isValid = true;
}
}
}
ViewModel.IsDraggingOverComposerGrid = false;
ViewModel.IsDraggingOverDropZone = false;
e.AcceptedOperation = isValid ? DataPackageOperation.Copy : DataPackageOperation.None;
if (isValid)
{
ViewModel.IsDraggingOverImagesDropZone = true;
e.DragUIOverride.Caption = Translator.ComposerAttachmentsDragDropAttach_Message;
e.DragUIOverride.IsCaptionVisible = true;
e.DragUIOverride.IsGlyphVisible = true;
e.DragUIOverride.IsContentVisible = true;
}
}
private void OnImageDropGridDragLeave(object sender, DragEventArgs e)
{
ViewModel.IsDraggingOverImagesDropZone = false;
}
private async void OnImageDropGridImageDropped(object sender, DragEventArgs e)
{
try
{
if (e.DataView.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await e.DataView.GetStorageItemsAsync();
var files = storageItems.OfType<StorageFile>();
var imageDataURLs = new List<string>();
foreach (var file in files)
{
if (ValidateImageFile(file))
imageDataURLs.Add(await GetDataURL(file));
}
await InvokeScriptSafeAsync($"insertImages({JsonConvert.SerializeObject(imageDataURLs)});");
}
}
// State should be reset even when an exception occurs, otherwise the UI will be stuck in a dragging state.
finally
{
ViewModel.IsDraggingOverComposerGrid = false;
ViewModel.IsDraggingOverImagesDropZone = false;
}
static async Task<string> GetDataURL(StorageFile file)
{
return $"data:image/{file.FileType.Replace(".", "")};base64,{Convert.ToBase64String(await file.ReadBytesAsync())}";
}
}
private async Task AttachFiles(IEnumerable<StorageFile> files)
@@ -165,49 +240,52 @@ namespace Wino.Views
}
}
private bool ValidateImageFile(StorageFile file)
{
string[] allowedTypes = new string[] { ".jpg", ".jpeg", ".png" };
var fileType = file.FileType.ToLower();
return allowedTypes.Contains(fileType);
}
private async void BoldButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('boldButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('bold')");
}
private async void ItalicButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('italicButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('italic')");
}
private async void UnderlineButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('underlineButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('underline')");
}
private async void StrokeButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('strikeButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('strikethrough')");
}
private async void BulletListButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('bulletListButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('insertunorderedlist')");
}
private async void OrderedListButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('orderedListButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('insertorderedlist')");
}
private async void IncreaseIndentClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('increaseIndentButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('indent')");
}
private async void DecreaseIndentClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('decreaseIndentButton').click();");
}
private async void DirectionButtonClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('directionButton').click();");
await InvokeScriptSafeAsync("editor.execCommand('outdent')");
}
private async void AlignmentChanged(object sender, SelectionChangedEventArgs e)
@@ -218,20 +296,26 @@ namespace Wino.Views
switch (alignment)
{
case "left":
await InvokeScriptSafeAsync("document.getElementById('ql-align-left').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifyleft')");
break;
case "center":
await InvokeScriptSafeAsync("document.getElementById('ql-align-center').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifycenter')");
break;
case "right":
await InvokeScriptSafeAsync("document.getElementById('ql-align-right').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifyright')");
break;
case "justify":
await InvokeScriptSafeAsync("document.getElementById('ql-align-justify').click();");
await InvokeScriptSafeAsync("editor.execCommand('justifyfull')");
break;
}
}
private async void WebViewToggleButtonClicked(object sender, RoutedEventArgs e)
{
var enable = WebviewToolBarButton.IsChecked == true ? "true" : "false";
await InvokeScriptSafeAsync($"toggleToolbar('{enable}');");
}
public async Task<string> ExecuteScriptFunctionAsync(string functionName, params object[] parameters)
{
string script = functionName + "(";
@@ -252,21 +336,24 @@ namespace Wino.Views
{
try
{
return await Chromium.ExecuteScriptAsync(function);
return await Chromium?.ExecuteScriptAsync(function);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception) { }
return string.Empty;
}
private async void AddImageClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync("document.getElementById('addImageButton').click();");
await InvokeScriptSafeAsync("imageInput.click();");
}
private async Task FocusEditorAsync()
{
await InvokeScriptSafeAsync("quill.focus();");
await InvokeScriptSafeAsync("editor.selection.focus();");
Chromium.Focus(FocusState.Keyboard);
Chromium.Focus(FocusState.Programmatic);
@@ -279,24 +366,6 @@ namespace Wino.Views
await FocusEditorAsync();
}
private async Task<string> TryGetSelectedTextAsync()
{
try
{
return await Chromium.ExecuteScriptAsync("getSelectedText();");
}
catch (Exception) { }
return string.Empty;
}
private async void LinkButtonClicked(object sender, RoutedEventArgs e)
{
// Get selected text from Quill.
HyperlinkTextBox.Text = await TryGetSelectedTextAsync();
}
public async Task UpdateEditorThemeAsync()
{
await DOMLoadedTask.Task;
@@ -346,7 +415,7 @@ namespace Wino.Views
if (Chromium.CoreWebView2 != null)
{
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved;
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived;
}
Chromium.Close();
@@ -379,9 +448,9 @@ namespace Wino.Views
ViewModel.GetHTMLBodyFunction = new Func<Task<string>>(async () =>
{
var quillContent = await InvokeScriptSafeAsync("GetHTMLContent();");
var editorContent = await InvokeScriptSafeAsync("GetHTMLContent();");
return JsonConvert.DeserializeObject<string>(quillContent);
return JsonConvert.DeserializeObject<string>(editorContent);
});
var underlyingThemeService = App.Current.Services.GetService<IUnderlyingThemeService>();
@@ -391,60 +460,66 @@ namespace Wino.Views
private async void ChromiumInitialized(Microsoft.UI.Xaml.Controls.WebView2 sender, Microsoft.UI.Xaml.Controls.CoreWebView2InitializedEventArgs args)
{
var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty);
var editorBundlePath = (await ViewModel.NativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.example", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
Chromium.Source = new Uri("https://app.example/full.html");
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.editor", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);
Chromium.Source = new Uri("https://app.editor/editor.html");
Chromium.CoreWebView2.DOMContentLoaded -= DOMLoaded;
Chromium.CoreWebView2.DOMContentLoaded += DOMLoaded;
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageRecieved;
Chromium.CoreWebView2.WebMessageReceived += ScriptMessageRecieved;
Chromium.CoreWebView2.WebMessageReceived -= ScriptMessageReceived;
Chromium.CoreWebView2.WebMessageReceived += ScriptMessageReceived;
}
private void ScriptMessageRecieved(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
private void ScriptMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args)
{
var change = JsonConvert.DeserializeObject<string>(args.WebMessageAsJson);
var change = JsonConvert.DeserializeObject<WebViewMessage>(args.WebMessageAsJson);
bool isEnabled = change.EndsWith("ql-active");
if (change.StartsWith("ql-bold"))
BoldButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-italic"))
ItalicButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-underline"))
UnderlineButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-strike"))
StrokeButton.IsChecked = isEnabled;
else if (change.StartsWith("orderedListButton"))
OrderedListButton.IsChecked = isEnabled;
else if (change.StartsWith("bulletListButton"))
BulletListButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-direction"))
DirectionButton.IsChecked = isEnabled;
else if (change.StartsWith("ql-align-left"))
if (change.type == "bold")
{
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 0;
AlignmentListView.SelectionChanged += AlignmentChanged;
BoldButton.IsChecked = change.value == "true";
}
else if (change.StartsWith("ql-align-center"))
else if (change.type == "italic")
{
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 1;
AlignmentListView.SelectionChanged += AlignmentChanged;
ItalicButton.IsChecked = change.value == "true";
}
else if (change.StartsWith("ql-align-right"))
else if (change.type == "underline")
{
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 2;
AlignmentListView.SelectionChanged += AlignmentChanged;
UnderlineButton.IsChecked = change.value == "true";
}
else if (change.StartsWith("ql-align-justify"))
else if (change.type == "strikethrough")
{
StrokeButton.IsChecked = change.value == "true";
}
else if (change.type == "ol")
{
OrderedListButton.IsChecked = change.value == "true";
}
else if (change.type == "ul")
{
BulletListButton.IsChecked = change.value == "true";
}
else if (change.type == "indent")
{
IncreaseIndentButton.IsEnabled = change.value == "disabled" ? false : true;
}
else if (change.type == "outdent")
{
DecreaseIndentButton.IsEnabled = change.value == "disabled" ? false : true;
}
else if (change.type == "alignment")
{
var parsedValue = change.value switch
{
"jodit-icon_left" => 0,
"jodit-icon_center" => 1,
"jodit-icon_right" => 2,
"jodit-icon_justify" => 3,
_ => 0
};
AlignmentListView.SelectionChanged -= AlignmentChanged;
AlignmentListView.SelectedIndex = 3;
AlignmentListView.SelectedIndex = parsedValue;
AlignmentListView.SelectionChanged += AlignmentChanged;
}
}
@@ -464,14 +539,6 @@ namespace Wino.Views
await RenderInternalAsync(message.RenderModel.RenderHtml);
}
private async void HyperlinkAddClicked(object sender, RoutedEventArgs e)
{
await InvokeScriptSafeAsync($"addHyperlink('{LinkUrlTextBox.Text}')");
LinkUrlTextBox.Text = string.Empty;
HyperlinkFlyout.Hide();
}
private void BarDynamicOverflowChanging(CommandBar sender, DynamicOverflowItemsChangingEventArgs args)
{
if (args.Action == CommandBarDynamicOverflowAction.AddingToOverflow)

View File

@@ -371,7 +371,7 @@
Grid.Row="1"
Background="{ThemeResource WinoContentZoneBackgroud}"
BorderBrush="{StaticResource CardStrokeColorDefaultBrush}"
BorderThickness="0"
BorderThickness="1"
CornerRadius="7">
<Grid Margin="1" CornerRadius="7">
<Grid Background="White" Visibility="{x:Bind IsDarkEditor, Converter={StaticResource ReverseBooleanToVisibilityConverter}, Mode=OneWay}" />

View File

@@ -184,7 +184,7 @@ namespace Wino.Views
{
if (Chromium.CoreWebView2 == null) return;
var editorBundlePath = (await ViewModel.NativeAppService.GetQuillEditorBundlePathAsync()).Replace("full.html", string.Empty);
var editorBundlePath = (await ViewModel.NativeAppService.GetEditorBundlePathAsync()).Replace("editor.html", string.Empty);
Chromium.CoreWebView2.SetVirtualHostNameToFolderMapping("app.reader", editorBundlePath, CoreWebView2HostResourceAccessKind.Allow);

View File

@@ -754,22 +754,14 @@
<Content Include="BackgroundImages\Mica.jpg" />
<Content Include="BackgroundImages\Nighty.jpg" />
<Content Include="BackgroundImages\Snowflake.jpg" />
<Content Include="JS\Quill\darkreader.js" />
<Content Include="JS\Quill\full.html" />
<Content Include="JS\Quill\global.css" />
<Content Include="JS\Quill\highlight.min.js" />
<Content Include="JS\Quill\image-resize.min.js" />
<Content Include="JS\Quill\katex.min.css" />
<Content Include="JS\Quill\katex.min.js" />
<Content Include="JS\Quill\monokai-sublime.min.css" />
<Content Include="JS\Quill\quill.core.css" />
<Content Include="JS\Quill\quill.core.js" />
<Content Include="JS\Quill\quill.js" />
<Content Include="JS\Quill\quill.min.js" />
<Content Include="JS\Quill\quill.snow.css" />
<Content Include="JS\libs\darkreader.js" />
<Content Include="JS\editor.html" />
<Content Include="JS\editor.js" />
<Content Include="JS\global.css" />
<Content Include="Assets\WinoIcons.ttf" />
<Content Include="JS\Quill\reader.css" />
<Content Include="JS\Quill\reader.html" />
<Content Include="JS\libs\jodit.min.css" />
<Content Include="JS\libs\jodit.min.js" />
<Content Include="JS\reader.html" />
<Content Include="Assets\ReleaseNotes\172.md" />
<None Include="Package.StoreAssociation.xml" />
<Content Include="Properties\Default.rd.xml" />
@@ -782,7 +774,6 @@
</ItemGroup>
<ItemGroup>
<Content Include="Assets\Thumbnails\uber.com.png" />
<None Include="JS\Quill\quill.min.js.map" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
@@ -817,9 +808,7 @@
<Name>Windows Desktop Extensions for the UWP</Name>
</SDKReference>
</ItemGroup>
<ItemGroup>
<Folder Include="JS\Quill\Fonts\" />
</ItemGroup>
<ItemGroup />
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
@@ -895,4 +884,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>