feat: inline url embeds in article editor (#14284)

This commit is contained in:
Sivin Varghese
2026-05-05 14:16:24 +05:30
committed by GitHub
parent c1d167bd64
commit 8cc36e1938
5 changed files with 97 additions and 19 deletions
@@ -13,6 +13,9 @@ import {
triggerCharacters,
} from '@chatwoot/prosemirror-schema/src/mentions/plugin';
import imagePastePlugin from '@chatwoot/prosemirror-schema/src/plugins/image';
import embedPreviewPlugin from '@chatwoot/prosemirror-schema/src/plugins/embedPreview';
import trailingParagraphPlugin from '@chatwoot/prosemirror-schema/src/plugins/trailingParagraph';
import { embeds as markdownEmbeds } from 'dashboard/helper/markdownEmbeds';
import { toggleMark } from 'prosemirror-commands';
import { wrapInList } from 'prosemirror-schema-list';
import { toggleBlockType } from '@chatwoot/prosemirror-schema/src/menu/common';
@@ -77,6 +80,8 @@ export default {
plugins: [
imagePastePlugin(this.handleImageUpload),
this.createSlashPlugin(),
embedPreviewPlugin(markdownEmbeds),
trailingParagraphPlugin(),
],
isTextSelected: false, // Tracks text selection and prevents unnecessary re-renders on mouse selection
showSlashMenu: false,
@@ -113,6 +118,12 @@ export default {
this.focusEditorInputField();
}
},
beforeUnmount() {
if (editorView) {
editorView.destroy();
editorView = null;
}
},
methods: {
createSlashPlugin() {
return suggestionsPlugin({
@@ -488,4 +499,9 @@ export default {
max-height: 7.5rem;
overflow: auto;
}
.ProseMirror .cw-embed-preview {
max-width: 36rem;
margin: 0.5rem 0 1rem;
}
</style>
@@ -0,0 +1,11 @@
import config from '../../../../config/markdown_embeds.yml';
// Gists rely on document.write() and can't render inline in the editor.
const NON_PREVIEWABLE_EMBEDS = new Set(['github_gist']);
export const embeds = Object.entries(config)
.filter(([key]) => !NON_PREVIEWABLE_EMBEDS.has(key))
.map(([, { regex, template }]) => ({
regex: new RegExp(regex),
template,
}));
+2 -1
View File
@@ -34,7 +34,7 @@
"@amplitude/analytics-browser": "^2.11.10",
"@breezystack/lamejs": "^1.2.7",
"@chatwoot/ninja-keys": "1.2.3",
"@chatwoot/prosemirror-schema": "1.3.10",
"@chatwoot/prosemirror-schema": "1.3.11",
"@chatwoot/utils": "^0.0.52",
"@formkit/core": "^1.7.2",
"@formkit/vue": "^1.7.2",
@@ -121,6 +121,7 @@
"@iconify-json/ri": "^1.2.6",
"@iconify-json/teenyicons": "^1.2.2",
"@intlify/eslint-plugin-vue-i18n": "^3.2.0",
"@rollup/plugin-yaml": "^4.1.2",
"@size-limit/file": "^8.2.4",
"@vitest/coverage-v8": "3.0.5",
"@vue/test-utils": "^2.4.6",
+65 -16
View File
@@ -26,8 +26,8 @@ importers:
specifier: 1.2.3
version: 1.2.3
'@chatwoot/prosemirror-schema':
specifier: 1.3.10
version: 1.3.10
specifier: 1.3.11
version: 1.3.11
'@chatwoot/utils':
specifier: ^0.0.52
version: 0.0.52
@@ -281,6 +281,9 @@ importers:
'@intlify/eslint-plugin-vue-i18n':
specifier: ^3.2.0
version: 3.2.0(eslint@8.57.0)
'@rollup/plugin-yaml':
specifier: ^4.1.2
version: 4.1.2(rollup@4.59.0)
'@size-limit/file':
specifier: ^8.2.4
version: 8.2.6(size-limit@8.2.6)
@@ -460,8 +463,8 @@ packages:
'@chatwoot/ninja-keys@1.2.3':
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
'@chatwoot/prosemirror-schema@1.3.10':
resolution: {integrity: sha512-MtOXqFPHptFHu/AoIPhQ9TskVXOxOXCgBY/tgAtCAQRut978F7I3QxozQBECBz83ubsoXnBecpNjGNq0OPgONw==}
'@chatwoot/prosemirror-schema@1.3.11':
resolution: {integrity: sha512-+GptIqY73/EtojrhAKX4UKwZF7NUB47DMzgYxmx8Vu07DLKCGe+8JnbHJnR9oBy8p5sn5VOO1ihNf/4Pg2RzIQ==}
'@chatwoot/utils@0.0.52':
resolution: {integrity: sha512-e57uVqyVW4tj1gql4YJPNMykqMJPkETn5Y9AmHdhc6Y7oxDXfRXBq27fZrrDadLkZdn5RYVCZjfIhXOumyYv2Q==}
@@ -1122,6 +1125,24 @@ packages:
'@rails/ujs@7.1.400':
resolution: {integrity: sha512-YwvXm3BR5tn+VCAKYGycLejMRVZE3Ionj5gFjEeGXCZnI0Rpi+7dKpmyu90kdUY7dRUFpHTdu9zZceEzFLl38w==}
'@rollup/plugin-yaml@4.1.2':
resolution: {integrity: sha512-RpupciIeZMUqhgFE97ba0s98mOFS7CWzN3EJNhJkqSv9XLlWYtwVdtE6cDw6ASOF/sZVFS7kRJXftaqM2Vakdw==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: '>=4.59.0'
peerDependenciesMeta:
rollup:
optional: true
'@rollup/pluginutils@5.3.0':
resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==}
engines: {node: '>=14.0.0'}
peerDependencies:
rollup: '>=4.59.0'
peerDependenciesMeta:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.59.0':
resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
cpu: [arm]
@@ -3016,8 +3037,8 @@ packages:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
js-yaml@4.1.0:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
jsdom@20.0.3:
@@ -3551,6 +3572,10 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
picomatch@4.0.4:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
pidtree@0.6.0:
resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
engines: {node: '>=0.10'}
@@ -4315,6 +4340,10 @@ packages:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
tosource@2.0.0-alpha.3:
resolution: {integrity: sha512-KAB2lrSS48y91MzFPFuDg4hLbvDiyTjOVgaK7Erw+5AmZXNq4sFRVn8r6yxSLuNs15PaokrDRpS61ERY9uZOug==}
engines: {node: '>=10'}
totalist@3.0.1:
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
engines: {node: '>=6'}
@@ -4976,7 +5005,7 @@ snapshots:
hotkeys-js: 3.8.7
lit: 2.2.6
'@chatwoot/prosemirror-schema@1.3.10':
'@chatwoot/prosemirror-schema@1.3.11':
dependencies:
markdown-it-sup: 2.0.0
prosemirror-commands: 1.7.1
@@ -5328,7 +5357,7 @@ snapshots:
globals: 13.24.0
ignore: 5.2.4
import-fresh: 3.3.0
js-yaml: 4.1.0
js-yaml: 4.1.1
minimatch: 3.1.5
strip-json-comments: 3.1.1
transitivePeerDependencies:
@@ -5342,7 +5371,7 @@ snapshots:
globals: 14.0.0
ignore: 5.2.4
import-fresh: 3.3.0
js-yaml: 4.1.0
js-yaml: 4.1.1
minimatch: 3.1.5
strip-json-comments: 3.1.1
transitivePeerDependencies:
@@ -5547,7 +5576,7 @@ snapshots:
ignore: 6.0.2
import-fresh: 3.3.0
is-language-code: 3.1.0
js-yaml: 4.1.0
js-yaml: 4.1.1
json5: 2.2.3
jsonc-eslint-parser: 2.4.0
lodash: 4.17.21
@@ -5689,6 +5718,22 @@ snapshots:
'@rails/ujs@7.1.400': {}
'@rollup/plugin-yaml@4.1.2(rollup@4.59.0)':
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.59.0)
js-yaml: 4.1.1
tosource: 2.0.0-alpha.3
optionalDependencies:
rollup: 4.59.0
'@rollup/pluginutils@5.3.0(rollup@4.59.0)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
picomatch: 4.0.4
optionalDependencies:
rollup: 4.59.0
'@rollup/rollup-android-arm-eabi@4.59.0':
optional: true
@@ -7184,7 +7229,7 @@ snapshots:
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
js-yaml: 4.1.0
js-yaml: 4.1.1
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2
@@ -7851,7 +7896,7 @@ snapshots:
argparse: 1.0.10
esprima: 4.0.1
js-yaml@4.1.0:
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
@@ -8421,6 +8466,8 @@ snapshots:
picomatch@2.3.1: {}
picomatch@4.0.4: {}
pidtree@0.6.0: {}
pify@2.3.0: {}
@@ -8723,7 +8770,7 @@ snapshots:
prosemirror-dropcursor@1.8.1:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.0
prosemirror-transform: 1.12.0
prosemirror-view: 1.34.1
prosemirror-gapcursor@1.3.2:
@@ -8736,14 +8783,14 @@ snapshots:
prosemirror-history@1.4.1:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.0
prosemirror-transform: 1.12.0
prosemirror-view: 1.34.1
rope-sequence: 1.3.2
prosemirror-inputrules@1.4.0:
dependencies:
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.0
prosemirror-transform: 1.12.0
prosemirror-keymap@1.2.2:
dependencies:
@@ -8783,7 +8830,7 @@ snapshots:
prosemirror-keymap: 1.2.2
prosemirror-model: 1.22.3
prosemirror-state: 1.4.3
prosemirror-transform: 1.10.0
prosemirror-transform: 1.12.0
prosemirror-view: 1.34.1
prosemirror-transform@1.10.0:
@@ -9308,6 +9355,8 @@ snapshots:
dependencies:
is-number: 7.0.0
tosource@2.0.0-alpha.3: {}
totalist@3.0.1: {}
tough-cookie@4.1.4:
+3 -2
View File
@@ -22,6 +22,7 @@ import { defineConfig } from 'vite';
import ruby from 'vite-plugin-ruby';
import path from 'path';
import vue from '@vitejs/plugin-vue';
import yaml from '@rollup/plugin-yaml';
const isLibraryMode = process.env.BUILD_MODE === 'library';
const isTestMode = process.env.TEST === 'true';
@@ -34,12 +35,12 @@ const vueOptions = {
},
};
let plugins = [ruby(), vue(vueOptions)];
let plugins = [ruby(), vue(vueOptions), yaml()];
if (isLibraryMode) {
plugins = [];
} else if (isTestMode) {
plugins = [vue(vueOptions)];
plugins = [vue(vueOptions), yaml()];
}
export default defineConfig({