diff --git a/app/javascript/dashboard/components-next/radioCard/RadioCard.vue b/app/javascript/dashboard/components-next/radioCard/RadioCard.vue
index 1f5398b15..5f6528f90 100644
--- a/app/javascript/dashboard/components-next/radioCard/RadioCard.vue
+++ b/app/javascript/dashboard/components-next/radioCard/RadioCard.vue
@@ -61,7 +61,7 @@ const handleChange = () => {
-
+
{{ label }}
diff --git a/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue b/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue
index 8bd3e9457..1c7787de6 100644
--- a/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue
+++ b/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue
@@ -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);
diff --git a/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue b/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue
index d74d889b2..fd168e7a6 100644
--- a/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue
+++ b/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue
@@ -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',
diff --git a/app/javascript/dashboard/i18n/locale/en/components.json b/app/javascript/dashboard/i18n/locale/en/components.json
index 68db1cc85..5e84ac024 100644
--- a/app/javascript/dashboard/i18n/locale/en/components.json
+++ b/app/javascript/dashboard/i18n/locale/en/components.json
@@ -63,6 +63,7 @@
"CODE": "Code",
"BULLET_LIST": "Bullet List",
"ORDERED_LIST": "Ordered List",
- "TABLE": "Table"
+ "TABLE": "Table",
+ "IMAGE": "Image"
}
}
diff --git a/lib/custom_markdown_renderer.rb b/lib/custom_markdown_renderer.rb
index d5c9a3351..665d4c80c 100644
--- a/lib/custom_markdown_renderer.rb
+++ b/lib/custom_markdown_renderer.rb
@@ -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(%(
')
+ 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
diff --git a/package.json b/package.json
index c6d2d95d2..ed8bbf00f 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b018dbdfe..3f1e9e2cf 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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
diff --git a/spec/lib/custom_markdown_renderer_spec.rb b/spec/lib/custom_markdown_renderer_spec.rb
index cb13f6cf9..28c5e069c 100644
--- a/spec/lib/custom_markdown_renderer_spec.rb
+++ b/spec/lib/custom_markdown_renderer_spec.rb
@@ -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 = ''
+ expect(render_markdown(markdown)).to include(
+ 'style="width: 400px; max-width: 100%; height: auto;"'
+ )
+ end
+
+ it 'ignores a non-numeric width' do
+ markdown = ''
+ expect(render_markdown(markdown)).not_to include('style=')
+ end
+ end
end