feat: add image resize support in articles (#14293)

This commit is contained in:
Sivin Varghese
2026-05-19 19:34:43 +05:30
committed by GitHub
parent 6560dbb68d
commit bca95efb82
8 changed files with 57 additions and 8 deletions
@@ -61,7 +61,7 @@ const handleChange = () => {
<div class="flex flex-col gap-2 items-start">
<div class="flex items-center justify-between w-full gap-3">
<div class="flex items-center gap-2">
<h3 class="text-heading-2 text-n-slate-12">
<h3 class="text-heading-3 text-n-slate-12">
{{ label }}
</h3>
<Label v-if="disabled" :label="disabledLabel" color="amber" compact />
@@ -7,6 +7,7 @@ import {
ArticleMarkdownTransformer,
EditorState,
Selection,
imageResizeView,
} from '@chatwoot/prosemirror-schema';
import {
suggestionsPlugin,
@@ -235,6 +236,7 @@ export default {
const tr = editorView.state.tr.replaceSelectionWith(tableNode);
editorView.dispatch(tr.scrollIntoView());
},
imageUpload: () => this.openFileBrowser(),
};
const command = commandMap[actionKey];
@@ -332,6 +334,9 @@ export default {
createEditorView() {
editorView = new EditorView(this.$refs.editor, {
state: state,
nodeViews: {
image: imageResizeView,
},
dispatchTransaction: tx => {
state = state.apply(tx);
editorView.updateState(state);
@@ -60,6 +60,12 @@ const EDITOR_ACTIONS = [
icon: 'i-lucide-table',
menuKey: 'insertTable',
},
{
value: 'imageUpload',
labelKey: 'SLASH_COMMANDS.IMAGE',
icon: 'i-lucide-image',
menuKey: 'imageUpload',
},
{
value: 'strike',
labelKey: 'SLASH_COMMANDS.STRIKETHROUGH',
@@ -63,6 +63,7 @@
"CODE": "Code",
"BULLET_LIST": "Bullet List",
"ORDERED_LIST": "Ordered List",
"TABLE": "Table"
"TABLE": "Table",
"IMAGE": "Image"
}
}
+23
View File
@@ -33,8 +33,31 @@ class CustomMarkdownRenderer < CommonMarker::HtmlRenderer
super
end
def image(node)
src = escape_href(node.url)
width = extract_image_width(src)
plain do
out(%(<img src="#{src}"))
out(' alt="', :children, '"')
out(%( title="#{escape_html(node.title)}")) if node.title.present?
out(%( style="width: #{width}; max-width: 100%; height: auto;")) if width
out(' />')
end
end
private
def extract_image_width(src)
query = URI.parse(src).query
raw = query && CGI.parse(query)['cw_image_width']&.first
return unless raw =~ /\A(\d+)px\z/
px = Regexp.last_match(1).to_i
"#{px}px" if px.between?(1, 2000)
rescue URI::InvalidURIError
nil
end
def surrounded_by_empty_lines?(node)
prev_node_empty?(node.previous) && next_node_empty?(node.next)
end
+1 -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.11",
"@chatwoot/prosemirror-schema": "1.3.13",
"@chatwoot/utils": "^0.0.52",
"@formkit/core": "^1.7.2",
"@formkit/vue": "^1.7.2",
+5 -5
View File
@@ -26,8 +26,8 @@ importers:
specifier: 1.2.3
version: 1.2.3
'@chatwoot/prosemirror-schema':
specifier: 1.3.11
version: 1.3.11
specifier: 1.3.13
version: 1.3.13
'@chatwoot/utils':
specifier: ^0.0.52
version: 0.0.52
@@ -459,8 +459,8 @@ packages:
'@chatwoot/ninja-keys@1.2.3':
resolution: {integrity: sha512-xM8d9P5ikDMZm2WbaCTk/TW5HFauylrU3cJ75fq5je6ixKwyhl/0kZbVN/vbbZN4+AUX/OaSIn6IJbtCgIF67g==}
'@chatwoot/prosemirror-schema@1.3.11':
resolution: {integrity: sha512-+GptIqY73/EtojrhAKX4UKwZF7NUB47DMzgYxmx8Vu07DLKCGe+8JnbHJnR9oBy8p5sn5VOO1ihNf/4Pg2RzIQ==}
'@chatwoot/prosemirror-schema@1.3.13':
resolution: {integrity: sha512-T6FBUinMJbwDCD7975g8M/Tsn2+G3O2pTGIXdcLkMRpbAAC6mVdl4ZcZektlt5y/PVmPVqNHPsfee1XB/C3vAw==}
'@chatwoot/utils@0.0.52':
resolution: {integrity: sha512-e57uVqyVW4tj1gql4YJPNMykqMJPkETn5Y9AmHdhc6Y7oxDXfRXBq27fZrrDadLkZdn5RYVCZjfIhXOumyYv2Q==}
@@ -4993,7 +4993,7 @@ snapshots:
hotkeys-js: 3.8.7
lit: 2.2.6
'@chatwoot/prosemirror-schema@1.3.11':
'@chatwoot/prosemirror-schema@1.3.13':
dependencies:
markdown-it-sup: 2.0.0
prosemirror-commands: 1.7.1
+14
View File
@@ -257,4 +257,18 @@ describe CustomMarkdownRenderer do
end
end
end
describe '#image' do
it 'renders width in px with responsive cap and auto height' do
markdown = '![Sample](https://example.com/image.jpg?cw_image_width=400px)'
expect(render_markdown(markdown)).to include(
'style="width: 400px; max-width: 100%; height: auto;"'
)
end
it 'ignores a non-numeric width' do
markdown = '![Sample](https://example.com/image.jpg?cw_image_width=auto)'
expect(render_markdown(markdown)).not_to include('style=')
end
end
end