diff --git a/.eslintignore b/.eslintignore
index 579a67e1d..968abb975 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -397,6 +397,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js.map
+packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.d.ts
+packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
+packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js.map
diff --git a/.gitignore b/.gitignore
index dc713b18a..027dcf6f8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -384,6 +384,9 @@ packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/styles/index.js.map
+packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.d.ts
+packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js
+packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.js.map
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.d.ts
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js
packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/useContextMenu.js.map
diff --git a/packages/app-cli/tests/html_to_md/sub_sup_insert_strikethrough.html b/packages/app-cli/tests/html_to_md/sub_sup_insert_strikethrough.html
new file mode 100644
index 000000000..d1149f97c
--- /dev/null
+++ b/packages/app-cli/tests/html_to_md/sub_sup_insert_strikethrough.html
@@ -0,0 +1 @@
+X1 X1 Insert Strike
\ No newline at end of file
diff --git a/packages/app-cli/tests/html_to_md/sub_sup_insert_strikethrough.md b/packages/app-cli/tests/html_to_md/sub_sup_insert_strikethrough.md
new file mode 100644
index 000000000..2a17e25d6
--- /dev/null
+++ b/packages/app-cli/tests/html_to_md/sub_sup_insert_strikethrough.md
@@ -0,0 +1 @@
+X1 X1 Insert ~~Strike~~
\ No newline at end of file
diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx
index 91fc3dc94..dbc118750 100644
--- a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx
+++ b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx
@@ -18,6 +18,7 @@ const { MarkupToHtml } = require('@joplin/renderer');
const taboverride = require('taboverride');
import { reg } from '@joplin/lib/registry';
import BaseItem from '@joplin/lib/models/BaseItem';
+import setupToolbarButtons from './utils/setupToolbarButtons';
const { themeStyle } = require('@joplin/lib/theme');
const { clipboard } = require('electron');
const supportedLocales = require('./supportedLocales');
@@ -543,7 +544,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const toolbarPluginButtons = pluginCommandNames.length ? ` | ${pluginCommandNames.join(' ')}` : '';
const toolbar = [
- 'bold', 'italic', 'joplinHighlight', '|',
+ 'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
'bullist', 'numlist', 'joplinChecklist', '|',
'h1', 'h2', 'h3', 'hr', 'blockquote', 'table', `joplinInsertDateTime${toolbarPluginButtons}`,
@@ -572,7 +573,11 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
contextmenu: false,
browser_spellcheck: true,
formats: {
- 'joplin-highlight': { inline: 'mark', remove: 'all' },
+ joplinHighlight: { inline: 'mark', remove: 'all' },
+ joplinStrikethrough: { inline: 's', remove: 'all' },
+ joplinInsert: { inline: 'ins', remove: 'all' },
+ joplinSub: { inline: 'sub', remove: 'all' },
+ joplinSup: { inline: 'sup', remove: 'all' },
},
setup: (editor: any) => {
@@ -647,18 +652,7 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
},
});
- editor.ui.registry.addToggleButton('joplinHighlight', {
- tooltip: _('Highlight'),
- icon: 'highlight-bg-color',
- onAction: async function() {
- editor.execCommand('mceToggleFormat', false, 'joplin-highlight');
- },
- onSetup: function(api: any) {
- editor.formatter.formatChanged('joplin-highlight', function(state: boolean) {
- api.setActive(state);
- });
- },
- });
+ setupToolbarButtons(editor);
editor.ui.registry.addButton('joplinCodeBlock', {
tooltip: _('Code Block'),
diff --git a/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.ts b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.ts
new file mode 100644
index 000000000..ef0943453
--- /dev/null
+++ b/packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.ts
@@ -0,0 +1,67 @@
+import { _ } from '@joplin/lib/locale';
+
+interface ButtonDefinition {
+ name: string;
+ tooltip: string;
+ icon: string;
+ grouped?: boolean;
+}
+
+function buttonDefinitions(): ButtonDefinition[] {
+ return [
+ {
+ name: 'joplinHighlight',
+ tooltip: _('Highlight'),
+ icon: 'highlight-bg-color',
+ },
+ {
+ name: 'joplinStrikethrough',
+ tooltip: _('Strikethrough'),
+ icon: 'strike-through',
+ },
+ {
+ name: 'joplinInsert',
+ tooltip: _('Insert'),
+ icon: 'underline',
+ grouped: true,
+ },
+ {
+ name: 'joplinSup',
+ tooltip: _('Superscript'),
+ icon: 'superscript',
+ grouped: true,
+ },
+ {
+ name: 'joplinSub',
+ tooltip: _('Subscript'),
+ icon: 'subscript',
+ grouped: true,
+ },
+ ];
+}
+
+export default function(editor: any) {
+ const definitions = buttonDefinitions();
+
+ for (const def of definitions) {
+ editor.ui.registry.addToggleButton(def.name, {
+ tooltip: def.tooltip,
+ icon: def.icon,
+ onAction: async function() {
+ editor.execCommand('mceToggleFormat', false, def.name);
+ },
+ onSetup: function(api: any) {
+ editor.formatter.formatChanged(def.name, function(state: boolean) {
+ api.setActive(state);
+ });
+ },
+ });
+ }
+
+ const items: string[] = definitions.filter(d => !!d.grouped).map(d => d.name);
+
+ editor.ui.registry.addGroupToolbarButton('formattingExtras', {
+ icon: 'image-options',
+ items: items.join(' '),
+ });
+}
diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts
index 654b460ba..949fe70fc 100644
--- a/packages/lib/models/Setting.ts
+++ b/packages/lib/models/Setting.ts
@@ -788,12 +788,12 @@ class Setting extends BaseModel {
'markdown.plugin.mark': { storage: SettingStorage.File, value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ==mark== syntax')}${wysiwygYes}` },
'markdown.plugin.footnote': { storage: SettingStorage.File, value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable footnotes')}${wysiwygNo}` },
'markdown.plugin.toc': { storage: SettingStorage.File, value: true, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable table of contents extension')}${wysiwygNo}` },
- 'markdown.plugin.sub': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ~sub~ syntax')}${wysiwygNo}` },
- 'markdown.plugin.sup': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ^sup^ syntax')}${wysiwygNo}` },
+ 'markdown.plugin.sub': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ~sub~ syntax')}${wysiwygYes}` },
+ 'markdown.plugin.sup': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ^sup^ syntax')}${wysiwygYes}` },
'markdown.plugin.deflist': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable deflist syntax')}${wysiwygNo}` },
'markdown.plugin.abbr': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable abbreviation syntax')}${wysiwygNo}` },
'markdown.plugin.emoji': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable markdown emoji')}${wysiwygNo}` },
- 'markdown.plugin.insert': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ++insert++ syntax')}${wysiwygNo}` },
+ 'markdown.plugin.insert': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ++insert++ syntax')}${wysiwygYes}` },
'markdown.plugin.multitable': { storage: SettingStorage.File, value: false, type: SettingItemType.Bool, section: 'markdownPlugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable multimarkdown table extension')}${wysiwygNo}` },
// Tray icon (called AppIndicator) doesn't work in Ubuntu
diff --git a/packages/turndown-plugin-gfm/src/strikethrough.js b/packages/turndown-plugin-gfm/src/strikethrough.js
index 5cbe40da5..9dfb9d7bb 100644
--- a/packages/turndown-plugin-gfm/src/strikethrough.js
+++ b/packages/turndown-plugin-gfm/src/strikethrough.js
@@ -2,7 +2,7 @@ export default function strikethrough (turndownService) {
turndownService.addRule('strikethrough', {
filter: ['del', 's', 'strike'],
replacement: function (content) {
- return '~' + content + '~'
+ return '~~' + content + '~~'
}
})
}
diff --git a/packages/turndown/src/commonmark-rules.js b/packages/turndown/src/commonmark-rules.js
index f68eb23fe..2d0fa2aaa 100644
--- a/packages/turndown/src/commonmark-rules.js
+++ b/packages/turndown/src/commonmark-rules.js
@@ -66,6 +66,36 @@ rules.highlight = {
}
}
+// Unlike strikethrough and mark formatting, insert, sup and sub aren't
+// widespread enough to automatically convert them to Markdown, but keep them as
+// HTML anyway. Another issue is that we use "~" for subscript but that's
+// actually the syntax for strikethrough on GitHub, so it's best to keep it as
+// HTML to avoid any ambiguity.
+
+rules.insert = {
+ filter: 'ins',
+
+ replacement: function (content, node, options) {
+ return '' + content + ''
+ }
+}
+
+rules.superscript = {
+ filter: 'sup',
+
+ replacement: function (content, node, options) {
+ return '' + content + ''
+ }
+}
+
+rules.subscript = {
+ filter: 'sub',
+
+ replacement: function (content, node, options) {
+ return '' + content + ''
+ }
+}
+
// ==============================
// END Joplin format support
// ==============================