Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cypress/e2e/shortcuts.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ describe('keyboard shortcuts', () => {
it('italic', () => testShortcut(`${modKey}i`, 'em'))
it('underline', () => testShortcut(`${modKey}u`, 'u'))
it('strikethrough', () => testShortcut(`${modKey}{shift}s`, 's'))
it('highlight', () => testShortcut(`${modKey}{shift}h`, 'mark'))
it('blockquote', () => testShortcut(`${modKey}{shift}b`, 'blockquote'))
it('codeblock', () => testShortcut(`${modKey}{alt}c`, 'pre'))
it('ordered-list', () => testShortcut(`${modKey}{shift}7`, 'ol'))
Expand Down
35 changes: 28 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@tiptap/extension-drag-handle-vue-2": "^3.19.0",
"@tiptap/extension-hard-break": "^3.19.0",
"@tiptap/extension-heading": "^3.19.0",
"@tiptap/extension-highlight": "^3.20.1",
"@tiptap/extension-horizontal-rule": "^3.19.0",
"@tiptap/extension-image": "^3.19.0",
"@tiptap/extension-italic": "^3.19.0",
Expand Down Expand Up @@ -88,6 +89,7 @@
"markdown-it-container": "^4.0.0",
"markdown-it-front-matter": "^0.2.4",
"markdown-it-image-figures": "^2.1.1",
"markdown-it-mark": "^4.0.0",
"markdown-it-multimd-table": "^4.2.3",
"mermaid": "^11.12.2",
"mitt": "^3.0.1",
Expand Down
1 change: 1 addition & 0 deletions playwright/e2e/format-text.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ new Map([
Italic: 'em',
Underline: 'u',
Strikethrough: 's',
Highlight: 'mark',
}
await editor.type('Format me')
for (const [button] of Object.entries(buttons)) {
Expand Down
19 changes: 16 additions & 3 deletions src/components/HelpModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@
<kbd>I</kbd>
</td>
</tr>
<tr>
<td>{{ t('text', 'Underline') }}</td>
<td>
<code>__{{ t('text', 'Underlined text') }}__</code>
</td>
<td v-if="!isMobileCached">
<kbd>{{ ctrlOrModKey }}</kbd>
+
<kbd>U</kbd>
</td>
</tr>
<tr>
<td>{{ t('text', 'Strikethrough') }}</td>
<td>
Expand All @@ -91,14 +102,16 @@
</td>
</tr>
<tr>
<td>{{ t('text', 'Underline') }}</td>
<td>{{ t('text', 'Highlight') }}</td>
<td>
<code>__{{ t('text', 'Underlined text') }}__</code>
<code>=={{ t('text', 'Highlighted text') }}==</code>
</td>
<td v-if="!isMobileCached">
<kbd>{{ ctrlOrModKey }}</kbd>
+
<kbd>U</kbd>
<kbd>{{ t('text', 'Shift') }}</kbd>
+
<kbd>H</kbd>
</td>
</tr>
<tr>
Expand Down
17 changes: 15 additions & 2 deletions src/components/Menu/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Danger,
Emoticon,
FormatBold,
FormatColorHighlight,
FormatHeader1,
FormatHeader2,
FormatHeader3,
Expand Down Expand Up @@ -282,6 +283,18 @@ export const getMenuEntries = (isRichWorkspace: boolean): MenuEntry[] => {
},
priority: 13,
},
{
key: 'highlight',
label: t('text', 'Highlight'),
keyChar: 'h',
keyModifiers: [MODIFIERS.Mod, MODIFIERS.Shift],
icon: FormatColorHighlight,
isActive: 'highlight',
action: (command) => {
return command.toggleHighlight()
},
priority: 14,
},
{
key: 'lists',
label: t('text', 'Lists'),
Expand Down Expand Up @@ -477,7 +490,7 @@ export const getMenuEntries = (isRichWorkspace: boolean): MenuEntry[] => {
action: (command) => {
return command.insertTable()
},
priority: 14,
priority: 15,
},
{
key: 'details',
Expand All @@ -487,7 +500,7 @@ export const getMenuEntries = (isRichWorkspace: boolean): MenuEntry[] => {
action: (command) => {
return command.toggleDetails()
},
priority: 15,
priority: 16,
},
{
key: 'insert-link',
Expand Down
2 changes: 2 additions & 0 deletions src/components/icons.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import MDI_Emoticon from 'vue-material-design-icons/EmoticonOutline.vue'
import MDI_Document from 'vue-material-design-icons/FileDocument.vue'
import MDI_Folder from 'vue-material-design-icons/FolderOutline.vue'
import MDI_FormatBold from 'vue-material-design-icons/FormatBold.vue'
import MDI_FormatColorHighlight from 'vue-material-design-icons/FormatColorHighlight.vue'
import MDI_FormatHeader1 from 'vue-material-design-icons/FormatHeader1.vue'
import MDI_FormatHeader2 from 'vue-material-design-icons/FormatHeader2.vue'
import MDI_FormatHeader3 from 'vue-material-design-icons/FormatHeader3.vue'
Expand Down Expand Up @@ -106,6 +107,7 @@ export const DotsHorizontal = makeIcon(MDI_DotsHorizontal)
export const Emoticon = makeIcon(MDI_Emoticon)
export const Folder = makeIcon(MDI_Folder)
export const FormatBold = makeIcon(MDI_FormatBold)
export const FormatColorHighlight = makeIcon(MDI_FormatColorHighlight)
export const FormatSize = makeIcon(MDI_FormatSize)
export const FormatHeader1 = makeIcon(MDI_FormatHeader1)
export const FormatHeader2 = makeIcon(MDI_FormatHeader2)
Expand Down
4 changes: 4 additions & 0 deletions src/css/prosemirror.scss
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ div.ProseMirror {
font-style: italic;
}

mark {
background-color: var(--color-mark, #fff0c7);
}

h1,
h2,
h3,
Expand Down
10 changes: 9 additions & 1 deletion src/extensions/RichText.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ import Mention from './../extensions/Mention.js'
import Search from './../extensions/Search.ts'
import TextDirection from './../extensions/TextDirection.ts'
import Typography from './../extensions/Typography.ts'
import { Italic, Link, Strike, Strong, Underline } from './../marks/index.js'
import {
Highlight,
Italic,
Link,
Strike,
Strong,
Underline,
} from './../marks/index.js'
import BulletList from './../nodes/BulletList.js'
import Callouts from './../nodes/Callouts.js'
import CodeBlock from './../nodes/CodeBlock.js'
Expand Down Expand Up @@ -73,6 +80,7 @@ export default Extension.create({
HardBreak,
Heading,
Strong,
Highlight,
Italic,
Strike,
Blockquote,
Expand Down
2 changes: 2 additions & 0 deletions src/markdownit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import markdownitMentions from '@quartzy/markdown-it-mentions'
import MarkdownIt from 'markdown-it'
import frontMatter from 'markdown-it-front-matter'
import implicitFigures from 'markdown-it-image-figures'
import mark from 'markdown-it-mark'
import multimdTable from 'markdown-it-multimd-table'
import { escapeHtml } from 'markdown-it/lib/common/utils.mjs'
import callouts from './callouts.js'
Expand All @@ -33,6 +34,7 @@ const markdownit = MarkdownIt('commonmark', { html: false, breaks: false })
.use(keepSyntax)
.use(markdownitMentions)
.use(implicitFigures)
.use(mark)
.use(mathematics)
.use(multimdTable, {
multiline: true,
Expand Down
18 changes: 18 additions & 0 deletions src/marks/Highlight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import TipTapHighlight from '@tiptap/extension-highlight'

const Highlight = TipTapHighlight.extend({
// @ts-expect-error - toMarkdown is a custom field not part of the official Tiptap API
toMarkdown: {
open: '==',
close: '==',
mixable: true,
expelEnclosingWhitespace: true,
},
})

export default Highlight
3 changes: 2 additions & 1 deletion src/marks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import TipTapItalic from '@tiptap/extension-italic'
import Highlight from './Highlight.ts'
import Link from './Link.js'
import Strike from './Strike.js'
import Strong from './Strong.js'
Expand All @@ -13,4 +14,4 @@ const Italic = TipTapItalic.extend({
name: 'em',
})

export { Italic, Link, Strike, Strong, Underline }
export { Highlight, Italic, Link, Strike, Strong, Underline }
36 changes: 36 additions & 0 deletions src/tests/marks/Highlight.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import createCustomEditor from '../testHelpers/createCustomEditor'
import Highlight from './../../marks/Highlight'

describe('Highlight extension unit', () => {
it('exposes toMarkdown function', () => {
// @ts-expect-error - toMarkdown is a custom field not part of the official Tiptap API
const toMarkdown = Highlight.config.toMarkdown
expect(JSON.stringify(toMarkdown)).to.equal(
JSON.stringify({
open: '==',
close: '==',
mixable: true,
expelEnclosingWhitespace: true,
}),
)
})
})

describe('Highlight extension integrated in the editor', () => {
it('is not active by default', () => {
const editor = createCustomEditor('<p>Test</p>', [Highlight])
expect(editor.isActive('highlight')).to.equal(false)
editor.destroy()
})

it('is active within <mark> tags', () => {
const editor = createCustomEditor('<p><mark>Test</mark></p>', [Highlight])
expect(editor.isActive('highlight')).to.equal(true)
editor.destroy()
})
})
Loading