diff --git a/NEWS.rst b/NEWS.rst
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_TkVXUy5yc3Q=..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_TkVXUy5yc3Q= 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -2,6 +2,11 @@
 Changelog
 =========
 
+18.0.1.2.0
+----------
+
+Improve dynamic placeholder implementation.
+
 18.0.1.1.2
 ----------
 
diff --git a/__manifest__.py b/__manifest__.py
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_X19tYW5pZmVzdF9fLnB5..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_X19tYW5pZmVzdF9fLnB5 100644
--- a/__manifest__.py
+++ b/__manifest__.py
@@ -21,7 +21,7 @@
 {
     "name": "Redner",
     "license": "AGPL-3",
-    "version": "18.0.1.1.2",
+    "version": "18.0.1.2.0",
     "category": "Reporting",
     "author": "XCG Consulting",
     "website": "https://orbeet.io/",
diff --git a/models/redner_substitution.py b/models/redner_substitution.py
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_bW9kZWxzL3JlZG5lcl9zdWJzdGl0dXRpb24ucHk=..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_bW9kZWxzL3JlZG5lcl9zdWJzdGl0dXRpb24ucHk= 100644
--- a/models/redner_substitution.py
+++ b/models/redner_substitution.py
@@ -83,7 +83,7 @@
 
     value_placeholder = fields.Char(compute="_compute_value_placeholder")
 
-    dynamic_placeholder_button_hidden = fields.Boolean(
-        compute="_compute_dynamic_placeholder_button_hidden"
+    hide_placeholder_button = fields.Boolean(
+        compute="_compute_hide_placeholder_button", string="Hide Placeholder Button"
     )
 
@@ -88,4 +88,9 @@
     )
 
+    @api.onchange("converter")
+    def _onchange_converter(self):
+        if self.converter:
+            self.value = False
+
     @api.depends("converter")
     def _compute_value_placeholder(self):
@@ -90,25 +95,18 @@
     @api.depends("converter")
     def _compute_value_placeholder(self):
-        """Computes a dynamic placeholder that depends on the selected type
-        to help the user inputs their data.
-        """
-        for subsitution in self:
-            placeholder = _("N/A")
-            if subsitution.converter == FIELD:
-                placeholder = _("e.g: name or partner_id.name")
-            elif subsitution.converter == MAIL_TEMPLATE:
-                placeholder = _("e.g: {{object.partner_id.name}}")
-            elif subsitution.converter == MAIL_TEMPLATE_DESERIALIZE:
-                placeholder = _("e.g: {{ object.get_partner_info() | safe }}")
-            elif subsitution.converter == RELATION_2MANY:
-                placeholder = _("e.g: tax_ids")
-            elif subsitution.converter == RELATION_PATH:
-                placeholder = _(
-                    "e.g: partner_id/category_id/name ou partner_id/child_ids[]"
-                )
-            elif subsitution.converter == CONSTANT:
-                placeholder = _("e.g: www.orbeet.io")
-            subsitution.value_placeholder = placeholder
+        """Compute placeholder text based on conversion type"""
+        placeholder_map = {
+            FIELD: _("e.g: name or partner_id.name"),
+            MAIL_TEMPLATE: _("e.g: {{object.partner_id.name}}"),
+            MAIL_TEMPLATE_DESERIALIZE: _("e.g: {{ object.get_partner_info() | safe }}"),
+            RELATION_PATH: _(
+                "e.g: partner_id/category_id/name ou partner_id/child_ids[]"
+            ),
+            RELATION_2MANY: _("e.g: tax_ids"),
+            CONSTANT: _("e.g: www.orbeet.io"),
+        }
+        for record in self:
+            record.value_placeholder = placeholder_map.get(record.converter, _("N/A"))
 
     @api.depends("value")
     def _compute_render_model(self):
@@ -126,5 +124,5 @@
             record.depth = record.keyword.count(".")
 
     @api.depends("converter")
-    def _compute_dynamic_placeholder_button_hidden(self):
+    def _compute_hide_placeholder_button(self):
         for record in self:
@@ -130,5 +128,5 @@
         for record in self:
-            record.dynamic_placeholder_button_hidden = (
+            record.hide_placeholder_button = (
                 record.converter not in DYNAMIC_PLACEHOLDER_ALLOWED_CONVERTERS
             )
 
diff --git a/static/src/components/dynamic_placeholder_charfield/dynamic_placeholder_charfield_patch.js b/static/src/components/dynamic_placeholder_char_field/dynamic_placeholder_char_field.js
similarity index 46%
rename from static/src/components/dynamic_placeholder_charfield/dynamic_placeholder_charfield_patch.js
rename to static/src/components/dynamic_placeholder_char_field/dynamic_placeholder_char_field.js
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_c3RhdGljL3NyYy9jb21wb25lbnRzL2R5bmFtaWNfcGxhY2Vob2xkZXJfY2hhcmZpZWxkL2R5bmFtaWNfcGxhY2Vob2xkZXJfY2hhcmZpZWxkX3BhdGNoLmpz..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_c3RhdGljL3NyYy9jb21wb25lbnRzL2R5bmFtaWNfcGxhY2Vob2xkZXJfY2hhcl9maWVsZC9keW5hbWljX3BsYWNlaG9sZGVyX2NoYXJfZmllbGQuanM= 100644
--- a/static/src/components/dynamic_placeholder_charfield/dynamic_placeholder_charfield_patch.js
+++ b/static/src/components/dynamic_placeholder_char_field/dynamic_placeholder_char_field.js
@@ -1,4 +1,4 @@
-/** @odoo-module **/
+/** @odoo-module */
 
 import {CharField, charField} from "@web/views/fields/char/char_field";
 import {patch} from "@web/core/utils/patch";
@@ -6,11 +6,12 @@
 // Adding a new property for dynamic placeholder button visibility
 CharField.props = {
     ...CharField.props,
-    dynamicPlaceholderButtonField: {type: String, optional: true},
-    dynamicPlaceholderConverterField: {type: String, optional: true},
+    rednerDynamicPlaceholder: {type: Boolean, optional: true}, // Dynamic placeholder flag
+    buttonVisibilityField: {type: String, optional: true},
+    converterField: {type: String, optional: true},
 };
 
 // Extending charField to extract the new property
 const charExtractProps = charField.extractProps;
 charField.extractProps = (fieldInfo) => {
     return Object.assign(charExtractProps(fieldInfo), {
@@ -11,13 +12,13 @@
 };
 
 // Extending charField to extract the new property
 const charExtractProps = charField.extractProps;
 charField.extractProps = (fieldInfo) => {
     return Object.assign(charExtractProps(fieldInfo), {
-        dynamicPlaceholderButtonField:
-            fieldInfo.options?.dynamic_placeholder_button_field,
-        dynamicPlaceholderConverterField:
-            fieldInfo.options?.dynamic_placeholder_converter_field,
+        rednerDynamicPlaceholder:
+            fieldInfo.options?.redner_dynamic_placeholder || false,
+        buttonVisibilityField: fieldInfo.options?.button_visibility_field || "",
+        converterField: fieldInfo.options?.converter_field || "",
     });
 };
 
@@ -26,6 +27,9 @@
     setup() {
         super.setup();
     },
-    get hasDynamicPlaceholderButton() {
-        return !this.props.record.data[this.props.dynamicPlaceholderButtonField];
+    get showMagicButton() {
+        const visibilityField = this.props.buttonVisibilityField;
+        // Return true if no visibility field is configured
+        if (!visibilityField) return true;
+        return this.props.record.data[visibilityField];
     },
@@ -31,9 +35,13 @@
     },
-    get converter() {
-        return (
-            this.props.record.data[this.props.dynamicPlaceholderConverterField] || ""
-        );
+    get hasRednerDynamicPlaceholder() {
+        return this.props.rednerDynamicPlaceholder && !this.props.readonly;
+    },
+    get hasDynamicPlaceholder() {
+        return super.hasDynamicPlaceholder && !this.hasRednerDynamicPlaceholder;
+    },
+    get activeConverterType() {
+        return this.props.record.data[this.props.converterField] || "";
     },
     async onDynamicPlaceholderValidate(chain, defaultValue) {
         if (chain) {
             this.input.el.focus();
@@ -36,10 +44,9 @@
     },
     async onDynamicPlaceholderValidate(chain, defaultValue) {
         if (chain) {
             this.input.el.focus();
-            // Initialize dynamicPlaceholder with a default structure
-            let dynamicPlaceholder = ` {{object.${chain}${
-                defaultValue?.length ? ` ||| ${defaultValue}` : ""
-            }}}`;
-            switch (this.converter) {
+
+            // Build placeholder based on converter type
+            let placeholder;
+            switch (this.activeConverterType) {
                 case "field":
@@ -45,4 +52,3 @@
                 case "field":
-                    // For "field" converter, use the chain directly as the value
-                    dynamicPlaceholder = `${chain}`;
+                    placeholder = `${chain}`;
                     break;
@@ -48,3 +54,3 @@
                     break;
-
+                // Add other converter types here
                 default:
@@ -50,8 +56,8 @@
                 default:
-                    // Default case if no specific converter type is found
-                    dynamicPlaceholder = ` {{object.${chain}${
-                        defaultValue?.length ? ` ||| ${defaultValue}` : ""
-                    }}}`;
+                    const defaultValuePart = defaultValue?.length
+                        ? ` ||| ${defaultValue}`
+                        : "";
+                    placeholder = `{{object.${chain}${defaultValuePart}}}`;
                     break;
             }
             this.input.el.setRangeText(
@@ -55,7 +61,7 @@
                     break;
             }
             this.input.el.setRangeText(
-                dynamicPlaceholder,
+                placeholder,
                 this.selectionStart,
                 this.selectionStart,
                 "end"
diff --git a/static/src/components/dynamic_placeholder_charfield/dynamic_placeholder_charfield_patch.xml b/static/src/components/dynamic_placeholder_char_field/dynamic_placeholder_char_field.xml
similarity index 27%
rename from static/src/components/dynamic_placeholder_charfield/dynamic_placeholder_charfield_patch.xml
rename to static/src/components/dynamic_placeholder_char_field/dynamic_placeholder_char_field.xml
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_c3RhdGljL3NyYy9jb21wb25lbnRzL2R5bmFtaWNfcGxhY2Vob2xkZXJfY2hhcmZpZWxkL2R5bmFtaWNfcGxhY2Vob2xkZXJfY2hhcmZpZWxkX3BhdGNoLnhtbA==..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_c3RhdGljL3NyYy9jb21wb25lbnRzL2R5bmFtaWNfcGxhY2Vob2xkZXJfY2hhcl9maWVsZC9keW5hbWljX3BsYWNlaG9sZGVyX2NoYXJfZmllbGQueG1s 100644
--- a/static/src/components/dynamic_placeholder_charfield/dynamic_placeholder_charfield_patch.xml
+++ b/static/src/components/dynamic_placeholder_char_field/dynamic_placeholder_char_field.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <templates xml:space="preserve">
     <t t-inherit="web.CharField" t-inherit-mode="extension">
-        <xpath expr="//button[@t-if='hasDynamicPlaceholder']" position="attributes">
-            <attribute name="t-if">
-                hasDynamicPlaceholder and hasDynamicPlaceholderButton
-            </attribute>
+        <xpath expr="//input" position="after">
+            <t t-call="redner.magicButton">
+                <t t-set="positionCenter" t-value="true" />
+            </t>
         </xpath>
     </t>
@@ -8,3 +8,16 @@
         </xpath>
     </t>
+    <t t-name="redner.magicButton">
+        <div class="position-relative d-inline" t-if="hasRednerDynamicPlaceholder">
+            <button
+                class="btn position-absolute end-0"
+                t-attf-class="{{positionCenter ? 'pb-0 pt-0' : 'bottom-0'}}"
+                t-att-disabled="showMagicButton"
+                t-ref="magicButton"
+                t-on-click="onDynamicPlaceholderOpen"
+            >
+                <i class="fa fa-magic" role="img" aria-label="Magic" />
+            </button>
+        </div>
+    </t>
 </templates>
diff --git a/static/src/js/redner_report_action.esm.js b/static/src/js/redner_report_action.esm.js
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_c3RhdGljL3NyYy9qcy9yZWRuZXJfcmVwb3J0X2FjdGlvbi5lc20uanM=..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_c3RhdGljL3NyYy9qcy9yZWRuZXJfcmVwb3J0X2FjdGlvbi5lc20uanM= 100644
--- a/static/src/js/redner_report_action.esm.js
+++ b/static/src/js/redner_report_action.esm.js
@@ -1,4 +1,5 @@
-/** @odoo-module **/
+/** @odoo-module */
+
 /*
 Redner Odoo module
 Copyright © 2016 XCG Consulting <https://xcg-consulting.fr>
diff --git a/utils/mimetype.py b/utils/mimetype.py
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_dXRpbHMvbWltZXR5cGUucHk=..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_dXRpbHMvbWltZXR5cGUucHk= 100644
--- a/utils/mimetype.py
+++ b/utils/mimetype.py
@@ -50,7 +50,11 @@
 
 
 def get_file_extension(binary_data: bytes) -> str:
-    """Determine the file extension from binary content."""
+    """Determine the file extension from binary content.
+
+    :param binary_data: Binary content of the file
+    :return: File extension string (default: .odt)
+    """
     file_type = guess_mimetype(binary_data)
 
     # Mapping MIME types to extensions
@@ -62,7 +66,4 @@
         "application/msword": ".doc",
         "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",  # noqa: E501
     }
-    # TODO the comment on line 64 is incorrect, it defaults to .odt
-    return mime_to_ext.get(
-        file_type, ".odt"
-    )  # Default to empty string if MIME type not found
+    return mime_to_ext.get(file_type, ".odt")  # Default to .odt if MIME type not found
diff --git a/views/ir_actions_report.xml b/views/ir_actions_report.xml
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_dmlld3MvaXJfYWN0aW9uc19yZXBvcnQueG1s..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_dmlld3MvaXJfYWN0aW9uc19yZXBvcnQueG1s 100644
--- a/views/ir_actions_report.xml
+++ b/views/ir_actions_report.xml
@@ -46,7 +46,7 @@
                                 <field name="render_model" column_invisible="1" />
                                 <field name="value_placeholder" column_invisible="1" />
                                 <field
-                                    name="dynamic_placeholder_button_hidden"
+                                    name="hide_placeholder_button"
                                     column_invisible="1"
                                 />
                                 <field
@@ -57,8 +57,9 @@
                                         'dynamic_placeholder': true, 
                                         'dynamic_placeholder_model_reference_field': 'render_model', 
                                         'placeholder_field': 'value_placeholder',
-                                        'dynamic_placeholder_button_field': 'dynamic_placeholder_button_hidden',
-                                        'dynamic_placeholder_converter_field': 'converter',
+                                        'redner_dynamic_placeholder': true,
+                                        'button_visibility_field': 'hide_placeholder_button',
+                                        'converter_field': 'converter',
                                     }"
                                     default_focus="1"
                                 />
diff --git a/views/mail_template.xml b/views/mail_template.xml
index e4b114d9ed795836b2ffefdc9ec32e7d92a07a9f_dmlld3MvbWFpbF90ZW1wbGF0ZS54bWw=..8898d80c29fe7a8765c4f0e2d96f9ffaf6feb3b0_dmlld3MvbWFpbF90ZW1wbGF0ZS54bWw= 100644
--- a/views/mail_template.xml
+++ b/views/mail_template.xml
@@ -42,7 +42,7 @@
                             <field name="render_model" column_invisible="1" />
                             <field name="value_placeholder" column_invisible="1" />
                             <field
-                                name="dynamic_placeholder_button_hidden"
+                                name="hide_placeholder_button"
                                 column_invisible="1"
                             />
                             <field
@@ -53,8 +53,9 @@
                                     'dynamic_placeholder': true,
                                     'dynamic_placeholder_model_reference_field': 'render_model'
                                     'placeholder_field': 'value_placeholder',
-                                    'dynamic_placeholder_button_field': 'dynamic_placeholder_button_hidden',
-                                    'dynamic_placeholder_converter_field': 'converter',
+                                    'redner_dynamic_placeholder': true,
+                                    'button_visibility_field': 'hide_placeholder_button',
+                                    'converter_field': 'converter',
                                 }"
                                 default_focus="1"
                             />