diff --git a/README.md b/README.md index 1ef32e74..2ce20346 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ 📖 **Documentation:** https://django-markdown-editor.readthedocs.io -**Martor** is a Markdown Editor plugin for Django, supported for _Bootstrap_ & _Semantic-UI_. +**Martor** is a Markdown Editor plugin for Django, supported for _Bootstrap_, _Semantic-UI_ & _Tailwind CSS_. ### Features * Live Preview * Integrated with [_Ace Editor_](https://ace.c9.io) -* Supported with [_Bootstrap_](https://getbootstrap.com) and [_Semantic-UI_](https://semantic-ui.com) +* Supported with [_Bootstrap_](https://getbootstrap.com), [_Semantic-UI_](https://semantic-ui.com) and [_Tailwind CSS_](https://tailwindcss.com) * Supported Multiple Fields [_fixed this issue_](https://github.com/agusmakmun/django-markdown-editor/issues/3) * Upload Images to imgur.com _(via API)_ and [custom uploader][13] * Direct Mention users `@[username]` - _(requires user to logged in)_. @@ -86,7 +86,7 @@ Please register your application at https://api.imgur.com/oauth2/addclient to get `IMGUR_CLIENT_ID` and `IMGUR_API_KEY`. ```python -# Choices are: "semantic", "bootstrap" +# Choices are: "semantic", "bootstrap", "tailwind" MARTOR_THEME = 'bootstrap' # Global martor settings @@ -335,6 +335,90 @@ Different with *Template Renderer*, the *Template Editor Form* have more css & j ``` +#### Tailwind CSS Theme + +When using `MARTOR_THEME = 'tailwind'`, include the required CSS and JavaScript files: + +**Template Renderer (Tailwind):** + +```html +{% extends "base.html" %} +{% load static %} +{% load martortags %} + +{% block css %} + + + +{% endblock %} + +{% block content %} +
+
+

{{ post.title }}

+
+ {{ post.description|safe_markdown }} +
+
+
+{% endblock %} + +{% block js %} + + +{% endblock %} +``` + +**Template Editor Form (Tailwind):** + +```html +{% extends "base.html" %} +{% load static %} + +{% block css %} + + + +{% endblock %} + +{% block content %} +
+
{% csrf_token %} +
+ + {{ form.title }} +
+
+ + {{ form.description }} +
+
+ +
+
+
+{% endblock %} + +{% block js %} + + + + + + + + + +{% endblock %} +``` + + ### Custom Uploader If you want to save the images uploaded to your storage, diff --git a/martor/static/martor/css/martor.tailwind.css b/martor/static/martor/css/martor.tailwind.css new file mode 100644 index 00000000..ca35ac97 --- /dev/null +++ b/martor/static/martor/css/martor.tailwind.css @@ -0,0 +1,717 @@ +/** + * Martor Tailwind CSS Theme + * This file provides Tailwind CSS-based styling for the Martor markdown editor + * Use with Tailwind CSS framework + */ + +/* Global Body Overflow Control */ +body.overflow { + overflow: hidden !important; +} + +/* Main Editor Container */ +.martor { + height: 500px; + max-height: 500px; +} + +.martor-field { + width: 100%; + height: 16rem; + /* 256px */ + min-height: 100px; + resize: vertical; + border-right: 1px solid #d1d5db; + border-bottom: 1px solid #d1d5db; +} + +.main-martor { + margin-top: 1rem; + margin-bottom: 1rem; + position: relative; +} + +/* Modal Styles */ +.main-martor .modal-header { + padding: 0.5rem 1rem; +} + +.main-martor .modal-header h5 { + font-size: 1rem; + line-height: 1.5rem; +} + +/* Help Guide Modal */ +.modal-help-guide { + z-index: 99999 !important; +} + +.modal-help-guide.flex { + display: flex !important; + position: fixed !important; + top: 0 !important; + left: 0 !important; + width: 100vw !important; + height: 100vh !important; + opacity: 1 !important; + visibility: visible !important; +} + +.modal-help-guide .bg-white { + display: block !important; + background-color: white !important; + border-radius: 12px !important; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important; + max-width: 900px !important; + width: 95% !important; + max-height: 90vh !important; + margin: 0 auto !important; + z-index: 20 !important; + visibility: visible !important; + opacity: 1 !important; + position: relative !important; + overflow: hidden !important; +} + +.modal-help-guide .flex.items-center { + display: flex !important; + align-items: center !important; + justify-content: center !important; + text-align: center !important; + width: 100% !important; + height: 100% !important; +} + +/* Enhanced Table Styling */ +.modal-help-guide table { + border-collapse: separate !important; + border-spacing: 0 !important; + width: 100% !important; + font-size: 14px !important; + line-height: 1.5 !important; +} + +.modal-help-guide th { + background-color: #f8fafc !important; + font-weight: 600 !important; + text-transform: uppercase !important; + letter-spacing: 0.05em !important; + font-size: 12px !important; + color: #64748b !important; + padding: 12px 16px !important; + border-bottom: 2px solid #e2e8f0 !important; + text-align: left !important; +} + +.modal-help-guide td { + padding: 16px !important; + border-bottom: 1px solid #f1f5f9 !important; + vertical-align: top !important; +} + +.modal-help-guide tr:hover td { + background-color: #f8fafc !important; +} + +.modal-help-guide code { + background-color: #f1f5f9 !important; + color: #475569 !important; + padding: 6px 10px !important; + border-radius: 6px !important; + font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Source Code Pro', monospace !important; + font-size: 13px !important; + font-weight: 500 !important; + border: 1px solid #e2e8f0 !important; + white-space: nowrap !important; +} + +.modal-help-guide kbd { + background-color: #334155 !important; + color: white !important; + padding: 4px 8px !important; + border-radius: 4px !important; + font-family: -apple-system, BlinkMacSystemFont, sans-serif !important; + font-size: 11px !important; + font-weight: 600 !important; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important; + border: 1px solid #1e293b !important; +} + +.modal-help-guide h1, +.modal-help-guide h2 { + margin: 0 !important; + font-weight: 700 !important; +} + +.modal-help-guide blockquote { + margin: 0 !important; + font-style: italic !important; + color: #64748b !important; +} + +.modal-help-guide ul, +.modal-help-guide ol { + margin: 0 !important; + padding-left: 20px !important; +} + +.modal-help-guide pre { + margin: 0 !important; + background-color: #f8fafc !important; + border: 1px solid #e2e8f0 !important; + border-radius: 6px !important; + padding: 12px !important; + font-size: 13px !important; + overflow-x: auto !important; +} + +.modal-help-guide hr { + border: none !important; + height: 2px !important; + background-color: #e2e8f0 !important; + margin: 8px 0 !important; +} + +/* Upload Progress Overlay */ +.main-martor .upload-progress { + position: absolute; + z-index: 100; + width: 100%; + height: 100%; + padding-top: 5rem; + background-color: rgba(0, 0, 0, 0.85); + color: white; + text-align: center; +} + +/* Toolbar Styles */ +.martor-toolbar { + z-index: 100; +} + +.enable-living .martor-toolbar { + position: relative; +} + +.martor-toolbar .markdown-image-upload { + position: relative; + overflow: hidden; +} + +.martor-toolbar .markdown-image-upload input[type=file] { + position: absolute; + top: 0; + right: 0; + width: 100%; + height: 100%; + font-size: 1.5rem; + padding: 0; + padding-left: 2.25rem; + text-align: right; + opacity: 0; + outline: none; + cursor: pointer; + display: block; + min-width: 100%; + min-height: 100%; + filter: alpha(opacity=0); +} + +/* Emoji Styles */ +.emoji-loader-init { + min-height: 200px !important; + @apply pt-16; +} + +.emoji-content-body { + @apply text-xs; +} + +.insert-emoji { + @apply cursor-pointer no-underline; +} + +/* Reference Table */ +.table.markdown-reference { + @apply text-xs; +} + +.table.markdown-reference h1 { + @apply text-2xl; +} + +.table.markdown-reference h2 { + @apply text-xl; +} + +.table.markdown-reference ul, +.table.markdown-reference ol { + @apply pl-4; +} + +/* Preview Styles */ +div.martor-preview { + @apply p-4 overflow-auto bg-gray-50; +} + +div.martor-preview-stale { + background: repeating-linear-gradient(-45deg, #fff, #fff 10px, #f8f8f8 10px, #f8f8f8 20px) !important; +} + +/* Tab Navigation Styles */ +.main-martor .nav-tabs { + @apply border-b-2 border-gray-200; +} + +.main-martor .nav-tabs .nav-link:hover, +.main-martor .nav-tabs .nav-link:focus { + @apply border-transparent; +} + +.main-martor .nav-tabs .nav-item.show .nav-link, +.main-martor .nav-tabs .nav-link.active, +.main-martor .nav-tabs .nav-link.active:hover { + @apply border-transparent border-b-2 border-gray-800 text-gray-700; +} + +.main-martor .tab-pane { + @apply relative; +} + +.icon.expand-editor { + @apply absolute -bottom-2 right-2 z-[100]; +} + +.no-border { + @apply border-0; +} + +/* Live Preview Mode */ +div.enable-living .martor-preview { + @apply block opacity-100 mt-4 border border-gray-200 rounded-md; +} + +div.enable-living .tab-martor-menu a.nav-link { + @apply hidden; +} + +/* Custom Scrollbar Styles */ +.section-martor ::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + @apply rounded-lg bg-gray-100; +} + +.section-martor ::-webkit-scrollbar { + @apply h-1.5 w-1.5 bg-gray-100; +} + +.section-martor ::-webkit-scrollbar-thumb { + @apply rounded-lg bg-gray-600; + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3); +} + +.ace_scrollbar-v { + cursor: ns-resize; +} + +/* Fullscreen Mode */ +.main-martor-fullscreen { + @apply bg-white fixed z-[9999] max-h-full h-full w-full m-0 left-0 top-0; +} + +.main-martor-fullscreen .fields.martor-toolbar { + @apply border-b border-gray-300 m-0; +} + +.main-martor-fullscreen .section-martor { + @apply h-[90%] relative; +} + +.main-martor-fullscreen .martor-field, +.main-martor-fullscreen div.martor-preview { + min-height: 95vh; +} + +/* Preview Content Styling */ +.marked-emoji, +div.martor-preview .marked-emoji { + @apply max-w-5; +} + +div.martor-preview { + @apply text-sm leading-relaxed; +} + +div.martor-preview>*:first-child { + @apply mt-0; +} + +div.martor-preview>*:last-child { + @apply mb-5; +} + +div.martor-preview a.absent { + @apply text-red-600; +} + +div.martor-preview a.anchor { + @apply block pl-8 -ml-8 cursor-pointer absolute top-0 left-0 bottom-0; +} + +/* Heading Styles */ +div.martor-preview h1, +div.martor-preview h2, +div.martor-preview h3, +div.martor-preview h4, +div.martor-preview h5, +div.martor-preview h6 { + @apply my-5 mb-2 p-0 font-bold cursor-text relative; + -webkit-font-smoothing: antialiased; + background: none; +} + +div.martor-preview h1:hover a.anchor, +div.martor-preview h2:hover a.anchor, +div.martor-preview h3:hover a.anchor, +div.martor-preview h4:hover a.anchor, +div.martor-preview h5:hover a.anchor, +div.martor-preview h6:hover a.anchor { + @apply no-underline leading-none pl-0 -ml-6; + top: 15%; +} + +div.martor-preview h1 { + @apply text-3xl text-black; +} + +div.martor-preview h2 { + @apply text-2xl text-black; +} + +div.martor-preview h3 { + @apply text-lg; +} + +div.martor-preview h4 { + @apply text-base; +} + +div.martor-preview h5 { + @apply text-sm; +} + +div.martor-preview h6 { + @apply text-gray-500 text-sm; +} + +/* Content Elements */ +div.martor-preview p, +div.martor-preview blockquote, +div.martor-preview ul, +div.martor-preview ol, +div.martor-preview dl, +div.martor-preview table, +div.martor-preview pre { + @apply my-4; +} + +div.martor-preview hr { + background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxBEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0; + @apply border-0 text-gray-300 h-1 p-0; + margin: 1rem 0; +} + +/* First Child Margin Reset */ +div.martor-preview>h2:first-child, +div.martor-preview>h1:first-child, +div.martor-preview>h1:first-child+h2, +div.martor-preview>h3:first-child, +div.martor-preview>h4:first-child, +div.martor-preview>h5:first-child, +div.martor-preview>h6:first-child { + @apply mt-0 pt-0; +} + +div.martor-preview a:first-child h1, +div.martor-preview a:first-child h2, +div.martor-preview a:first-child h3, +div.martor-preview a:first-child h4, +div.martor-preview a:first-child h5, +div.martor-preview a:first-child h6 { + @apply mt-0 pt-0; +} + +div.martor-preview h1+p, +div.martor-preview h2+p, +div.martor-preview h3+p, +div.martor-preview h4+p, +div.martor-preview h5+p, +div.martor-preview h6+p { + @apply mt-0; +} + +/* List Styles */ +div.martor-preview li p.first { + @apply inline-block; +} + +div.martor-preview ul { + @apply pl-8; + margin: 1rem 0; + list-style-type: disc !important; + list-style-position: inside !important; + display: block !important; +} + +div.martor-preview ol { + @apply pl-8; + margin: 1rem 0; + list-style-type: decimal !important; + list-style-position: inside !important; + display: block !important; +} + +div.martor-preview ul li, +div.martor-preview ol li { + margin: 0.25rem 0; + padding-left: 0.5rem; + display: list-item !important; +} + +div.martor-preview ul.no-list, +div.martor-preview ol.no-list { + @apply list-none p-0; +} + +/* Nested list styles */ +div.martor-preview ul li> :first-child, +div.martor-preview ul li ul:first-of-type, +div.martor-preview ol li> :first-child, +div.martor-preview ol li ul:first-of-type { + @apply mt-0; +} + +div.martor-preview ul ul, +div.martor-preview ul ol, +div.martor-preview ol ol, +div.martor-preview ol ul { + @apply mb-0; + margin-top: 0.5rem; +} + +/* Ensure nested lists have proper indentation */ +div.martor-preview li>ul, +div.martor-preview li>ol { + @apply pl-4; + margin: 0.25rem 0; +} + +/* Definition Lists */ +div.martor-preview dl { + @apply p-0; +} + +div.martor-preview dl dt { + @apply text-sm font-bold italic p-0 my-4 mt-0; +} + +div.martor-preview dl dt:first-child { + @apply p-0; +} + +div.martor-preview dl dt> :first-child { + @apply mt-0; +} + +div.martor-preview dl dt> :last-child { + @apply mb-0; +} + +div.martor-preview dl dd { + @apply m-0 mb-4 pl-4; +} + +div.martor-preview dl dd> :first-child { + @apply mt-0; +} + +div.martor-preview dl dd> :last-child { + @apply mb-0; +} + +/* Blockquotes */ +div.martor-preview blockquote { + border-left: 4px solid #d1d5db !important; + padding: 0.25rem 1rem !important; + margin: 1rem 0 !important; + background-color: #f9fafb !important; + color: #6b7280 !important; + font-style: italic !important; + border-radius: 0 0.375rem 0.375rem 0 !important; +} + +div.martor-preview blockquote p { + margin: 0.5rem 0 !important; +} + +div.martor-preview blockquote> :first-child { + margin-top: 0 !important; +} + +div.martor-preview blockquote> :last-child { + margin-bottom: 0 !important; +} + +/* Tables */ +div.martor-preview table th { + @apply font-bold; +} + +div.martor-preview table th, +div.martor-preview table td { + @apply border border-gray-300 py-1.5 px-3; +} + +div.martor-preview table tr { + @apply border-t border-gray-300 bg-white; +} + +div.martor-preview table tr:nth-child(2n) { + @apply bg-gray-50; +} + +/* Images */ +div.martor-preview img, +div.martor-preview p img { + @apply max-w-full; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +/* Image Frames */ +div.martor-preview span.frame { + @apply block overflow-hidden; +} + +div.martor-preview span.frame>span { + @apply border border-gray-300 block float-left overflow-hidden my-3 mt-0 p-2 w-auto; +} + +div.martor-preview span.frame span img { + @apply block float-left; +} + +div.martor-preview span.frame span span { + @apply clear-both text-gray-800 block pt-1 pb-0; +} + +/* Image Alignment */ +div.martor-preview span.align-center { + @apply block overflow-hidden clear-both; +} + +div.martor-preview span.align-center>span { + @apply block overflow-hidden my-3 mt-0 mx-auto text-center; +} + +div.martor-preview span.align-center span img { + @apply mx-auto text-center; +} + +div.martor-preview span.align-right { + @apply block overflow-hidden clear-both; +} + +div.martor-preview span.align-right>span { + @apply block overflow-hidden my-3 mt-0 text-right; +} + +div.martor-preview span.align-right span img { + @apply m-0 text-right; +} + +div.martor-preview span.float-left { + @apply block mr-3 overflow-hidden float-left; +} + +div.martor-preview span.float-left span { + @apply my-3 mt-0; +} + +div.martor-preview span.float-right { + @apply block ml-3 overflow-hidden float-right; +} + +div.martor-preview span.float-right>span { + @apply block overflow-hidden my-3 mt-0 mx-auto text-right; +} + +/* Code Styles */ +div.martor-preview code, +div.martor-preview tt { + @apply mx-0.5 py-0 px-1 border border-gray-200 bg-gray-100 rounded; +} + +div.martor-preview code { + @apply whitespace-nowrap; +} + +div.martor-preview pre>code { + @apply m-0 p-0 whitespace-pre border-0 bg-transparent; +} + +div.martor-preview .highlight pre, +div.martor-preview pre { + @apply border border-gray-200 p-4 overflow-auto text-sm leading-normal bg-gray-50 rounded; +} + +div.martor-preview pre code, +div.martor-preview pre tt { + @apply m-0 p-0 bg-transparent border-0; +} + +/* Form Integration Styles */ +/* Enhanced form styling for better integration */ +.form-field input[type="text"], +.form-field textarea, +form input[type="text"]:not(.martor input), +form textarea:not(.martor textarea) { + @apply w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm; + @apply focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500; + @apply transition-colors duration-200; +} + +/* Error state styling */ +.has-error input, +.has-error textarea { + @apply border-red-300 focus:ring-red-500 focus:border-red-500; +} + +/* Disabled state */ +input:disabled, +textarea:disabled { + @apply bg-gray-50 text-gray-500 cursor-not-allowed; +} + +/* Enhanced Martor Editor Styling */ +.main-martor { + border: 1px solid #d1d5db; + border-radius: 0.375rem; + overflow: hidden; +} + +.main-martor .tab-martor-menu { + background-color: #f9fafb; + border-bottom: 1px solid #e5e7eb; +} + +/* Force martor field height */ +.main-martor .martor-field { + height: 16rem !important; + min-height: 16rem !important; +} + +.main-martor .ace_editor { + height: 100% !important; + min-height: 16rem !important; +} diff --git a/martor/static/martor/css/martor.tailwind.min.css b/martor/static/martor/css/martor.tailwind.min.css new file mode 100644 index 00000000..56b88c81 --- /dev/null +++ b/martor/static/martor/css/martor.tailwind.min.css @@ -0,0 +1,9 @@ +/** + * Name : Martor v1.6.45 + * Created by : Agus Makmun (Summon Agus) + * Release date : 15-Nov-2024 + * License : GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + * Repository : https://github.com/agusmakmun/django-markdown-editor + * CSS Minifier : https://www.minifier.org +**/ +body.overflow{overflow:hidden!important}.martor{height:500px;max-height:500px}.martor-field{width:100%;height:16rem;min-height:100px;resize:vertical;border-right:1px solid #d1d5db;border-bottom:1px solid #d1d5db}.main-martor{margin-top:1rem;margin-bottom:1rem;position:relative}.main-martor .modal-header{padding:.5rem 1rem}.main-martor .modal-header h5{font-size:1rem;line-height:1.5rem}.modal-help-guide{z-index:99999!important}.modal-help-guide.flex{display:flex!important;position:fixed!important;top:0!important;left:0!important;width:100vw!important;height:100vh!important;opacity:1!important;visibility:visible!important}.modal-help-guide .bg-white{display:block!important;background-color:white!important;border-radius:12px!important;box-shadow:0 25px 50px -12px rgb(0 0 0 / .25)!important;max-width:900px!important;width:95%!important;max-height:90vh!important;margin:0 auto!important;z-index:20!important;visibility:visible!important;opacity:1!important;position:relative!important;overflow:hidden!important}.modal-help-guide .flex.items-center{display:flex!important;align-items:center!important;justify-content:center!important;text-align:center!important;width:100%!important;height:100%!important}.modal-help-guide table{border-collapse:separate!important;border-spacing:0!important;width:100%!important;font-size:14px!important;line-height:1.5!important}.modal-help-guide th{background-color:#f8fafc!important;font-weight:600!important;text-transform:uppercase!important;letter-spacing:0.05em!important;font-size:12px!important;color:#64748b!important;padding:12px 16px!important;border-bottom:2px solid #e2e8f0!important;text-align:left!important}.modal-help-guide td{padding:16px!important;border-bottom:1px solid #f1f5f9!important;vertical-align:top!important}.modal-help-guide tr:hover td{background-color:#f8fafc!important}.modal-help-guide code{background-color:#f1f5f9!important;color:#475569!important;padding:6px 10px!important;border-radius:6px!important;font-family:'SF Mono','Monaco','Inconsolata','Roboto Mono','Source Code Pro',monospace!important;font-size:13px!important;font-weight:500!important;border:1px solid #e2e8f0!important;white-space:nowrap!important}.modal-help-guide kbd{background-color:#334155!important;color:white!important;padding:4px 8px!important;border-radius:4px!important;font-family:-apple-system,BlinkMacSystemFont,sans-serif!important;font-size:11px!important;font-weight:600!important;box-shadow:0 2px 4px rgb(0 0 0 / .1)!important;border:1px solid #1e293b!important}.modal-help-guide h1,.modal-help-guide h2{margin:0!important;font-weight:700!important}.modal-help-guide blockquote{margin:0!important;font-style:italic!important;color:#64748b!important}.modal-help-guide ul,.modal-help-guide ol{margin:0!important;padding-left:20px!important}.modal-help-guide pre{margin:0!important;background-color:#f8fafc!important;border:1px solid #e2e8f0!important;border-radius:6px!important;padding:12px!important;font-size:13px!important;overflow-x:auto!important}.modal-help-guide hr{border:none!important;height:2px!important;background-color:#e2e8f0!important;margin:8px 0!important}.main-martor .upload-progress{position:absolute;z-index:100;width:100%;height:100%;padding-top:5rem;background-color:rgb(0 0 0 / .85);color:#fff;text-align:center}.martor-toolbar{z-index:100}.enable-living .martor-toolbar{position:relative}.martor-toolbar .markdown-image-upload{position:relative;overflow:hidden}.martor-toolbar .markdown-image-upload input[type=file]{position:absolute;top:0;right:0;width:100%;height:100%;font-size:1.5rem;padding:0;padding-left:2.25rem;text-align:right;opacity:0;outline:none;cursor:pointer;display:block;min-width:100%;min-height:100%;filter:alpha(opacity=0)}.emoji-loader-init{min-height:200px!important;@apply pt-16}.emoji-content-body{@apply text-xs}.insert-emoji{@apply cursor-pointer no-underline}.table.markdown-reference{@apply text-xs}.table.markdown-reference h1{@apply text-2xl}.table.markdown-reference h2{@apply text-xl}.table.markdown-reference ul,.table.markdown-reference ol{@apply pl-4}div.martor-preview{@apply p-4 overflow-auto bg-gray-50}div.martor-preview-stale{background:repeating-linear-gradient(-45deg,#fff,#fff 10px,#f8f8f8 10px,#f8f8f8 20px)!important}.main-martor .nav-tabs{@apply border-b-2 border-gray-200}.main-martor .nav-tabs .nav-link:hover,.main-martor .nav-tabs .nav-link:focus{@apply border-transparent}.main-martor .nav-tabs .nav-item.show .nav-link,.main-martor .nav-tabs .nav-link.active,.main-martor .nav-tabs .nav-link.active:hover{@apply border-transparent border-b-2 border-gray-800 text-gray-700}.main-martor .tab-pane{@apply relative}.icon.expand-editor{@apply absolute -bottom-2 right-2 z-[100]}.no-border{@apply border-0}div.enable-living .martor-preview{@apply block opacity-100 mt-4 border border-gray-200 rounded-md}div.enable-living .tab-martor-menu a.nav-link{@apply hidden}.section-martor ::-webkit-scrollbar-track{-webkit-box-shadow:inset 0 0 6px rgb(0 0 0 / .3);@apply rounded-lg bg-gray-100}.section-martor ::-webkit-scrollbar{@apply h-1.5 w-1.5 bg-gray-100}.section-martor ::-webkit-scrollbar-thumb{@apply rounded-lg bg-gray-600;-webkit-box-shadow:inset 0 0 6px rgb(0 0 0 / .3)}.ace_scrollbar-v{cursor:ns-resize}.main-martor-fullscreen{@apply bg-white fixed z-[9999] max-h-full h-full w-full m-0 left-0 top-0}.main-martor-fullscreen .fields.martor-toolbar{@apply border-b border-gray-300 m-0}.main-martor-fullscreen .section-martor{@apply h-[90%] relative}.main-martor-fullscreen .martor-field,.main-martor-fullscreen div.martor-preview{min-height:95vh}.marked-emoji,div.martor-preview .marked-emoji{@apply max-w-5}div.martor-preview{@apply text-sm leading-relaxed}div.martor-preview>*:first-child{@apply mt-0}div.martor-preview>*:last-child{@apply mb-5}div.martor-preview a.absent{@apply text-red-600}div.martor-preview a.anchor{@apply block pl-8 -ml-8 cursor-pointer absolute top-0 left-0 bottom-0}div.martor-preview h1,div.martor-preview h2,div.martor-preview h3,div.martor-preview h4,div.martor-preview h5,div.martor-preview h6{@apply my-5 mb-2 p-0 font-bold cursor-text relative;-webkit-font-smoothing:antialiased;background:none}div.martor-preview h1:hover a.anchor,div.martor-preview h2:hover a.anchor,div.martor-preview h3:hover a.anchor,div.martor-preview h4:hover a.anchor,div.martor-preview h5:hover a.anchor,div.martor-preview h6:hover a.anchor{@apply no-underline leading-none pl-0 -ml-6;top:15%}div.martor-preview h1{@apply text-3xl text-black}div.martor-preview h2{@apply text-2xl text-black}div.martor-preview h3{@apply text-lg}div.martor-preview h4{@apply text-base}div.martor-preview h5{@apply text-sm}div.martor-preview h6{@apply text-gray-500 text-sm}div.martor-preview p,div.martor-preview blockquote,div.martor-preview ul,div.martor-preview ol,div.martor-preview dl,div.martor-preview table,div.martor-preview pre{@apply my-4}div.martor-preview hr{background:#fff0 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxBEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC) repeat-x 0 0;@apply border-0 text-gray-300 h-1 p-0;margin:1rem 0}div.martor-preview>h2:first-child,div.martor-preview>h1:first-child,div.martor-preview>h1:first-child+h2,div.martor-preview>h3:first-child,div.martor-preview>h4:first-child,div.martor-preview>h5:first-child,div.martor-preview>h6:first-child{@apply mt-0 pt-0}div.martor-preview a:first-child h1,div.martor-preview a:first-child h2,div.martor-preview a:first-child h3,div.martor-preview a:first-child h4,div.martor-preview a:first-child h5,div.martor-preview a:first-child h6{@apply mt-0 pt-0}div.martor-preview h1+p,div.martor-preview h2+p,div.martor-preview h3+p,div.martor-preview h4+p,div.martor-preview h5+p,div.martor-preview h6+p{@apply mt-0}div.martor-preview li p.first{@apply inline-block}div.martor-preview ul{@apply pl-8;margin:1rem 0;list-style-type:disc!important;list-style-position:inside!important;display:block!important}div.martor-preview ol{@apply pl-8;margin:1rem 0;list-style-type:decimal!important;list-style-position:inside!important;display:block!important}div.martor-preview ul li,div.martor-preview ol li{margin:.25rem 0;padding-left:.5rem;display:list-item!important}div.martor-preview ul.no-list,div.martor-preview ol.no-list{@apply list-none p-0}div.martor-preview ul li>:first-child,div.martor-preview ul li ul:first-of-type,div.martor-preview ol li>:first-child,div.martor-preview ol li ul:first-of-type{@apply mt-0}div.martor-preview ul ul,div.martor-preview ul ol,div.martor-preview ol ol,div.martor-preview ol ul{@apply mb-0;margin-top:.5rem}div.martor-preview li>ul,div.martor-preview li>ol{@apply pl-4;margin:.25rem 0}div.martor-preview dl{@apply p-0}div.martor-preview dl dt{@apply text-sm font-bold italic p-0 my-4 mt-0}div.martor-preview dl dt:first-child{@apply p-0}div.martor-preview dl dt>:first-child{@apply mt-0}div.martor-preview dl dt>:last-child{@apply mb-0}div.martor-preview dl dd{@apply m-0 mb-4 pl-4}div.martor-preview dl dd>:first-child{@apply mt-0}div.martor-preview dl dd>:last-child{@apply mb-0}div.martor-preview blockquote{border-left:4px solid #d1d5db!important;padding:.25rem 1rem!important;margin:1rem 0!important;background-color:#f9fafb!important;color:#6b7280!important;font-style:italic!important;border-radius:0 .375rem .375rem 0!important}div.martor-preview blockquote p{margin:.5rem 0!important}div.martor-preview blockquote>:first-child{margin-top:0!important}div.martor-preview blockquote>:last-child{margin-bottom:0!important}div.martor-preview table th{@apply font-bold}div.martor-preview table th,div.martor-preview table td{@apply border border-gray-300 py-1.5 px-3}div.martor-preview table tr{@apply border-t border-gray-300 bg-white}div.martor-preview table tr:nth-child(2n){@apply bg-gray-50}div.martor-preview img,div.martor-preview p img{@apply max-w-full;-moz-box-sizing:border-box;box-sizing:border-box}div.martor-preview span.frame{@apply block overflow-hidden}div.martor-preview span.frame>span{@apply border border-gray-300 block float-left overflow-hidden my-3 mt-0 p-2 w-auto}div.martor-preview span.frame span img{@apply block float-left}div.martor-preview span.frame span span{@apply clear-both text-gray-800 block pt-1 pb-0}div.martor-preview span.align-center{@apply block overflow-hidden clear-both}div.martor-preview span.align-center>span{@apply block overflow-hidden my-3 mt-0 mx-auto text-center}div.martor-preview span.align-center span img{@apply mx-auto text-center}div.martor-preview span.align-right{@apply block overflow-hidden clear-both}div.martor-preview span.align-right>span{@apply block overflow-hidden my-3 mt-0 text-right}div.martor-preview span.align-right span img{@apply m-0 text-right}div.martor-preview span.float-left{@apply block mr-3 overflow-hidden float-left}div.martor-preview span.float-left span{@apply my-3 mt-0}div.martor-preview span.float-right{@apply block ml-3 overflow-hidden float-right}div.martor-preview span.float-right>span{@apply block overflow-hidden my-3 mt-0 mx-auto text-right}div.martor-preview code,div.martor-preview tt{@apply mx-0.5 py-0 px-1 border border-gray-200 bg-gray-100 rounded}div.martor-preview code{@apply whitespace-nowrap}div.martor-preview pre>code{@apply m-0 p-0 whitespace-pre border-0 bg-transparent}div.martor-preview .highlight pre,div.martor-preview pre{@apply border border-gray-200 p-4 overflow-auto text-sm leading-normal bg-gray-50 rounded}div.martor-preview pre code,div.martor-preview pre tt{@apply m-0 p-0 bg-transparent border-0}.form-field input[type="text"],.form-field textarea,form input[type="text"]:not(.martor input),form textarea:not(.martor textarea){@apply w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm;@apply focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500;@apply transition-colors duration-200}.has-error input,.has-error textarea{@apply border-red-300 focus:ring-red-500 focus:border-red-500}input:disabled,textarea:disabled{@apply bg-gray-50 text-gray-500 cursor-not-allowed}.main-martor{border:1px solid #d1d5db;border-radius:.375rem;overflow:hidden}.main-martor .tab-martor-menu{background-color:#f9fafb;border-bottom:1px solid #e5e7eb}.main-martor .martor-field{height:16rem!important;min-height:16rem!important}.main-martor .ace_editor{height:100%!important;min-height:16rem!important} diff --git a/martor/static/martor/js/martor.tailwind.js b/martor/static/martor/js/martor.tailwind.js new file mode 100644 index 00000000..f1a4c5e7 --- /dev/null +++ b/martor/static/martor/js/martor.tailwind.js @@ -0,0 +1,1087 @@ +/** + * Name : Martor v1.6.45 (Tailwind CSS Version) + * Created by : Agus Makmun (Summon Agus) + * Release date : 15-Nov-2024 + * License : GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + * Repository : https://github.com/agusmakmun/django-markdown-editor + * Theme : Tailwind CSS +**/ + +(function ($) { + if (!$) { + $ = django.jQuery; + } + $.fn.martor = function () { + $('.martor').trigger('martor.init'); + + // CSRF code + var getCookie = function (name) { + var cookieValue = null; + var i = 0; + if (document.cookie && document.cookie !== '') { + var cookies = document.cookie.split(';'); + for (i; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + }; + + // Tailwind-specific utility functions + var TailwindUtils = { + showElement: function(element) { + $(element).removeClass('hidden').addClass('block'); + }, + hideElement: function(element) { + $(element).removeClass('block').addClass('hidden'); + }, + showModal: function(element) { + console.log('Showing modal:', element); + var $modal = $(element); + $modal.removeClass('hidden').addClass('flex'); + $modal.css({ + 'display': 'flex !important', + 'z-index': '99999', + 'position': 'fixed', + 'top': '0', + 'left': '0', + 'width': '100vw', + 'height': '100vh', + 'opacity': '1', + 'visibility': 'visible' + }); + $('body').addClass('overflow-hidden'); + console.log('Modal styles applied:', $modal.css(['display', 'z-index', 'position'])); + console.log('Modal visibility:', $modal.is(':visible')); + console.log('Modal dimensions:', { + width: $modal.outerWidth(), + height: $modal.outerHeight(), + offset: $modal.offset() + }); + // Also ensure the modal content is visible + $modal.find('.bg-white').css({ + 'display': 'block', + 'position': 'relative', + 'z-index': '10' + }); + + // Force the modal container to be visible + $modal.show().fadeIn(); + + // Ensure modal content panel is visible + var modalPanel = $modal.find('.bg-white').first(); + var modalContainer = $modal.find('.flex.items-center'); + + modalContainer.css({ + 'display': 'flex !important', + 'align-items': 'center', + 'justify-content': 'center', + 'width': '100%', + 'height': '100%', + 'text-align': 'center' + }); + + modalPanel.css({ + 'display': 'block !important', + 'visibility': 'visible', + 'opacity': '1', + 'background-color': 'white', + 'max-width': '800px', + 'width': '90%', + 'z-index': '20', + 'margin': '0', + 'border-radius': '8px', + 'box-shadow': '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + 'transform': 'none', + 'position': 'relative' + }); + + console.log('Modal panel found:', modalPanel.length); + console.log('Modal container found:', modalContainer.length); + console.log('Modal panel styles:', modalPanel.css(['display', 'visibility', 'background-color'])); + + // Force all content to be visible + $modal.find('*').css('visibility', 'visible'); + }, + hideModal: function(element) { + console.log('Hiding modal:', element); + $(element).removeClass('flex').addClass('hidden').css('display', 'none'); + $('body').removeClass('overflow-hidden'); + }, + toggleTab: function(activeTab, activeContent, inactiveTabs, inactiveContents) { + // Hide all tabs and contents + $(inactiveTabs).removeClass('border-b-2 border-gray-800 text-gray-700').addClass('text-gray-500'); + $(inactiveContents).removeClass('block').addClass('hidden'); + + // Show active tab and content + $(activeTab).removeClass('text-gray-500').addClass('border-b-2 border-gray-800 text-gray-700'); + $(activeContent).removeClass('hidden').addClass('block'); + } + }; + + // Each multiple editor fields + this.each(function (i, obj) { + var mainMartor = $(obj); + var field_name = mainMartor.data('field-name'); + var textareaId = $('#id_' + field_name); + var editorId = 'martor-' + field_name; + var editor = ace.edit(editorId); + var editorConfig = JSON.parse(textareaId.data('enable-configs').replace(/'/g, '"')); + + editor.setTheme('ace/theme/github'); + editor.getSession().setMode('ace/mode/markdown'); + editor.getSession().setUseWrapMode(true); + editor.$blockScrolling = Infinity; // prevents ace from logging annoying warnings + editor.renderer.setScrollMargin(10, 10); // set padding + editor.setAutoScrollEditorIntoView(true); + editor.setShowPrintMargin(false); + editor.setOptions({ + enableBasicAutocompletion: true, + enableSnippets: true, + enableLiveAutocompletion: true, + enableMultiselect: false + }); + + if (editorConfig.living == 'true') { + $(obj).addClass('enable-living'); + } + + var emojiWordCompleter = { + getCompletions: function (editor, session, pos, prefix, callback) { + var wordList = typeof (emojis) != "undefined" ? emojis : []; // from `atwho/emojis.min.js` + var obj = editor.getSession().getTokenAt(pos.row, pos.column.count); + if (typeof (obj.value) != "undefined") { + var curTokens = obj.value.split(/\s+/); + var lastToken = curTokens[curTokens.length - 1]; + + if (lastToken[0] == ':') { + callback(null, wordList.map(function (word) { + return { + caption: word, + value: word.replace(':', '') + ' ', + meta: 'emoji' // this should return as text only. + }; + })); + } + } + } + } + var mentionWordCompleter = { + getCompletions: function (editor, session, pos, prefix, callback) { + var obj = editor.getSession().getTokenAt(pos.row, pos.column.count); + if (typeof (obj.value) != "undefined") { + var curTokens = obj.value.split(/\s+/); + var lastToken = curTokens[curTokens.length - 1]; + + if (lastToken[0] == '@' && lastToken[1] == '[') { + username = lastToken.replace(/([\@\[/\]/])/g, ''); + $.ajax({ + url: textareaId.data('search-users-url'), + data: { + 'username': username, + 'csrfmiddlewaretoken': getCookie('csrftoken') + }, + success: function (data) { + if (data['status'] == 200) { + var wordList = []; + for (var i = 0; i < data['data'].length; i++) { + wordList.push(data['data'][i].username) + } + callback(null, wordList.map(function (word) { + return { + caption: word, + value: word, + meta: 'username' + }; + })); + } + }// end success + }); + } + } + } + } + + // Set autocomplete for ace editor + if (editorConfig.mention === 'true') { + editor.completers = [emojiWordCompleter, mentionWordCompleter] + } else { + editor.completers = [emojiWordCompleter] + } + + // set css `display:none` for this textarea. + textareaId.attr({ 'style': 'display:none' }); + + // assign all `field_name`, uses for a per-single editor. + $(obj).find('.martor-toolbar').find('.markdown-selector').attr({ 'data-field-name': field_name }); + $(obj).find('.upload-progress').attr({ 'data-field-name': field_name }); + $(obj).find('.modal-help-guide').attr({ 'data-field-name': field_name }); + $(obj).find('.modal-emoji').attr({ 'data-field-name': field_name }); + + // Set if editor has changed. + editor.on('change', function (evt) { + var value = editor.getValue(); + textareaId.val(value); + }); + + // update the preview if this menu is clicked + var currentTab = $('#preview-content-' + field_name); + var editorTabButton = $('.tab-editor-' + field_name); + var previewTabButton = $('.tab-preview-' + field_name); + var toolbarButtons = $(this).closest('.tab-martor-menu').find('.martor-toolbar'); + + // Tailwind-specific tab handling + $('.tab-editor-' + field_name).click(function() { + var editorContent = $('#editor-content-' + field_name); + var previewContent = $('#preview-content-' + field_name); + var previewTab = $('.tab-preview-' + field_name); + + TailwindUtils.toggleTab( + this, + editorContent, + previewTab, + previewContent + ); + + // show the `.martor-toolbar` for this current editor if under preview. + $(this).closest('.tab-martor-menu').find('.martor-toolbar').show(); + }); + + $('.tab-preview-' + field_name).click(function() { + var editorContent = $('#editor-content-' + field_name); + var previewContent = $('#preview-content-' + field_name); + var editorTab = $('.tab-editor-' + field_name); + + TailwindUtils.toggleTab( + this, + previewContent, + editorTab, + editorContent + ); + + // hide the `.martor-toolbar` when activating tab preview. + $(this).closest('.tab-martor-menu').find('.martor-toolbar').hide(); + refreshPreview(); + }); + + var refreshPreview = function () { + var value = textareaId.val(); + var form = new FormData(); + form.append('content', value); + form.append('csrfmiddlewaretoken', getCookie('csrftoken')); + currentTab.addClass('martor-preview-stale'); + + $.ajax({ + url: textareaId.data('markdownfy-url'), + type: 'POST', + data: form, + processData: false, + contentType: false, + success: function (response) { + if (response) { + currentTab.html(response).removeClass('martor-preview-stale'); + currentTab.addClass('martor-preview'); + $(document).trigger('martor:preview', [currentTab]); + + if (editorConfig.hljs == 'true') { + $('pre').each(function (i, block) { + hljs.highlightBlock(block); + }); + } + } else { + currentTab.html('

Nothing to preview

'); + } + }, + error: function (response) { + console.log("error", response); + } + }); + }; + + let timeoutID; + + var refreshPreviewTimeout = function () { + if (timeoutID) { + clearTimeout(timeoutID); + } + timeoutID = setTimeout(refreshPreview, textareaId.data("save-timeout")); + } + + // Refresh the preview unconditionally on first load. + window.onload = function () { + refreshPreview(); + }; + + if (editorConfig.living !== 'true') { + previewTabButton.click(function () { + // hide the `.martor-toolbar` for this current editor if under preview. + toolbarButtons.hide(); + refreshPreview(); + }); + } else { + editor.on('change', refreshPreviewTimeout); + } + + // spellcheck + if (editorConfig.spellcheck == 'true') { + try { + enable_spellcheck(editorId); + } catch (e) { + console.log("Spellcheck lib doesn't installed."); + } + } + + if (editorConfig.emoji == 'true') { + langTools = ace.require("ace/ext/language_tools"); + langTools.addCompleter(emojiWordCompleter); + } + + if (editorConfig.mention == 'true') { + langTools = ace.require("ace/ext/language_tools"); + langTools.addCompleter(mentionWordCompleter); + } + + // Handle toolbar dropdowns for Tailwind + $('.martor-toolbar .dropdown').each(function() { + var dropdown = $(this); + var toggle = dropdown.find('.dropdown-toggle'); + var menu = dropdown.find('.dropdown-menu'); + + toggle.click(function(e) { + e.preventDefault(); + e.stopPropagation(); + + // Close other dropdowns + $('.martor-toolbar .dropdown-menu').not(menu).addClass('hidden'); + + // Toggle current dropdown + menu.toggleClass('hidden'); + }); + + // Close dropdown when clicking outside + $(document).click(function(e) { + if (!dropdown.is(e.target) && dropdown.has(e.target).length === 0) { + menu.addClass('hidden'); + } + }); + }); + + // Modal Popup for Help Guide & Emoji Cheat Sheet + $('.markdown-help[data-field-name=' + field_name + ']').click(function (e) { + e.preventDefault(); + console.log('Help button clicked for field:', field_name); + var modalSelector = '.modal-help-guide[data-field-name=' + field_name + ']'; + console.log('Modal selector:', modalSelector); + var modal = $(modalSelector); + console.log('Modal found:', modal.length); + TailwindUtils.showModal(modalSelector); + }); + + // Close modal functionality + $('.modal-help-guide[data-field-name=' + field_name + '] .modal-close').click(function (e) { + e.preventDefault(); + TailwindUtils.hideModal('.modal-help-guide[data-field-name=' + field_name + ']'); + }); + + // Also handle click on modal backdrop to close + $('.modal-help-guide[data-field-name=' + field_name + ']').click(function (e) { + if (e.target === this || $(e.target).hasClass('bg-gray-500')) { + TailwindUtils.hideModal('.modal-help-guide[data-field-name=' + field_name + ']'); + } + }); + + // Toggle editor, preview, maximize + var martorField = $('.martor-field-' + field_name); + var btnToggleMaximize = $('.markdown-toggle-maximize[data-field-name=' + field_name + ']'); + + // Toggle maximize and minimize + var handleToggleMinimize = function () { + $(document.body).removeClass('overflow-hidden'); + $(this).attr({ 'title': 'Full Screen' }); + $(this).find('svg.bi-arrows-angle-expand').removeClass('hidden'); + $(this).find('svg.bi-arrows-angle-contract').addClass('hidden'); + $('.main-martor-fullscreen').find('.martor-preview').removeAttr('style'); + mainMartor.removeClass('main-martor-fullscreen'); + martorField.removeAttr('style'); + editor.resize(); + } + var handleToggleMaximize = function (selector) { + selector.attr({ 'title': 'Minimize' }); + selector.find('svg.bi-arrows-angle-expand').addClass('hidden'); + selector.find('svg.bi-arrows-angle-contract').removeClass('hidden'); + mainMartor.addClass('main-martor-fullscreen'); + + var clientHeight = document.body.clientHeight - 90; + martorField.attr({ 'style': 'height:' + clientHeight + 'px' }); + + var preview = $('.main-martor-fullscreen').find('.martor-preview'); + preview.attr({ 'style': 'overflow-y: auto;height:' + clientHeight + 'px' }); + + editor.resize(); + selector.one('click', handleToggleMinimize); + $(document.body).addClass('overflow-hidden'); + } + btnToggleMaximize.on('click', function () { + handleToggleMaximize($(this)); + }); + + // Exit full screen when `ESC` is pressed. + $(document).keyup(function (e) { + if (e.keyCode == 27 && mainMartor.hasClass('main-martor-fullscreen')) { + btnToggleMaximize.trigger('click'); + } + }); + + // markdown insert emoji from the modal + $('.markdown-emoji[data-field-name=' + field_name + ']').click(function () { + var modalEmoji = $('.modal-emoji[data-field-name=' + field_name + ']'); + TailwindUtils.showModal(modalEmoji); + var emojiList = typeof (emojis) != "undefined" ? emojis : []; // from `plugins/js/emojis.min.js` + var emojiGrid = modalEmoji.find('#emoji-grid-' + field_name); + var loaderInit = modalEmoji.find('.emoji-loader-init'); + + // Only load emojis if grid is empty + if (emojiGrid.children().length === 0) { + // Show loader + TailwindUtils.showElement(loaderInit); + + // Load emojis with timeout to show loading state + setTimeout(function() { + try { + console.log('Loading emojis, count:', emojiList.length); + + for (var i = 0; i < Math.min(emojiList.length, 300); i++) { // Increased to 300 for more emojis + var emoji = emojiList[i]; + var linkEmoji = textareaId.data('base-emoji-url') + emoji.replace(/:/g, '') + '.png'; + + var emojiButton = $(''); + + emojiGrid.append(emojiButton); + } + + // Attach click handlers to dynamically created buttons + modalEmoji.find('.insert-emoji').off('click').on('click', function() { + var emojiTarget = $(this).data('emoji-target'); + console.log('Emoji selected:', emojiTarget); + markdownToEmoji(editor, emojiTarget); + TailwindUtils.hideModal(modalEmoji); + }); + + TailwindUtils.hideElement(loaderInit); + console.log('Emojis loaded successfully'); + } catch (error) { + console.error('Error loading emojis:', error); + TailwindUtils.hideElement(loaderInit); + emojiGrid.html('
Error loading emojis. Please try again.
'); + } + }, 100); // Small delay to show loading state + } else { + console.log('Emojis already loaded'); + } + }); + + // Close emoji modal + $('.modal-emoji[data-field-name=' + field_name + '] .modal-close').click(function () { + TailwindUtils.hideModal('.modal-emoji[data-field-name=' + field_name + ']'); + }); + + // Handle popular emoji buttons + $('.modal-emoji[data-field-name=' + field_name + '] .insert-emoji-popular').click(function () { + var emojiTarget = $(this).data('emoji-target'); + markdownToEmoji(editor, emojiTarget); + TailwindUtils.hideModal('.modal-emoji[data-field-name=' + field_name + ']'); + }); + + // Set initial value if has the content before. + editor.setValue(textareaId.val(), -1); + + // win/linux: Ctrl+B, mac: Command+B + var markdownToBold = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' **** '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 3); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '**' + text + '**'); + originalRange.end.column += 4; // this because injected from 4 `*` characters. + editor.focus(); + editor.selection.setSelectionRange(originalRange); + } + }; + // win/linux: Ctrl+I, mac: Command+I + var markdownToItalic = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' __ '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 2); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '_' + text + '_'); + originalRange.end.column += 2; // this because injected from 2 `_` characters. + editor.focus(); + editor.selection.setSelectionRange(originalRange); + } + }; + // win/linux: Ctrl+Shift+U, mac: Command+Shift+U + var markdownToUnderscores = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' ++++ '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 3); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '++' + text + '++'); + originalRange.end.column += 4; // this because injected from 4 `*` characters. + editor.focus(); + editor.selection.setSelectionRange(originalRange); + } + }; + // win/linux: Ctrl+Shift+S, mac: Command+Shift+S + var markdownToStrikethrough = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' ~~~~ '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 3); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '~~' + text + '~~'); + originalRange.end.column += 4; // this because injected from 4 `*` characters. + editor.focus(); + editor.selection.setSelectionRange(originalRange); + } + }; + // win/linux: Ctrl+H, mac: Command+H + var markdownToHorizontal = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n----------\n\n'); + editor.focus(); + editor.selection.moveTo(curpos.row + 4, curpos.column + 10); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n----------\n\n' + text); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 4, + originalRange.end.column + 10 + ); + } + }; + // win/linux: Ctrl+Alt+1, mac: Command+Option+1 + var markdownToH1 = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n# '); + editor.focus(); + editor.selection.moveTo(curpos.row + 2, curpos.column + 2); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n# ' + text + '\n'); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 2, + originalRange.end.column + 2 + ); + } + }; + // win/linux: Ctrl+Alt+2, mac: Command+Option+2 + var markdownToH2 = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n## '); + editor.focus(); + editor.selection.moveTo(curpos.row + 2, curpos.column + 3); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n## ' + text + '\n'); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 2, + originalRange.end.column + 3 + ); + } + }; + // win/linux: Ctrl+Alt+3, mac: Command+Option+3 + var markdownToH3 = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n### '); + editor.focus(); + editor.selection.moveTo(curpos.row + 2, curpos.column + 4); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n### ' + text + '\n'); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 2, + originalRange.end.column + 4 + ); + } + }; + // win/linux: Ctrl+Alt+P, mac: Command+Option+P + var markdownToPre = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n```\n\n```\n'); + editor.focus(); + editor.selection.moveTo(curpos.row + 3, curpos.column); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n```\n' + text + '\n```\n'); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 3, + originalRange.end.column + 3 + ); + } + }; + // win/linux: Ctrl+Alt+C, mac: Command+Option+C + var markdownToCode = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' `` '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 2); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '`' + text + '`'); + originalRange.end.column += 2; // this because injected from 2 `_` characters. + editor.focus(); + editor.selection.setSelectionRange(originalRange); + } + }; + // win/linux: Ctrl+Q, mac: Command+Shift+K + var markdownToBlockQuote = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n> \n'); + editor.focus(); + editor.selection.moveTo(curpos.row + 2, curpos.column + 2); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n> ' + text + '\n'); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 2, + originalRange.end.column + 2 + ); + } + }; + // win/linux: Ctrl+U, mac: Command+U + var markdownToUnorderedList = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n* '); + editor.focus(); + editor.selection.moveTo(curpos.row + 2, curpos.column + 2); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n* ' + text); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 2, + originalRange.end.column + 2 + ); + } + }; + // win/linux: Ctrl+Shift+O, mac: Command+Option+O + var markdownToOrderedList = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '\n\n1. '); + editor.focus(); + editor.selection.moveTo(curpos.row + 2, curpos.column + 3); + } + else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '\n\n1. ' + text); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row + 2, + originalRange.end.column + 3 + ); + } + }; + // win/linux: Ctrl+L, mac: Command+L + var markdownToLink = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' [](https://) '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 2); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '[' + text + '](https://) '); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row, + originalRange.end.column + 11 + ); + } + }; + // win/linux: Ctrl+Shift+I, mac: Command+Option+I + // or via upload: imageData={name:null, link:null} + var markdownToImageLink = function (editor, imageData) { + var originalRange = editor.getSelectionRange(); + if (typeof (imageData) === 'undefined') { + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' ![](https://) '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 3); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '![' + text + '](https://) '); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row, + originalRange.end.column + 12 + ); + } + } else { // this if use image upload to imgur. + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, '![' + imageData.name + '](' + imageData.link + ') '); + editor.focus(); + editor.selection.moveTo( + curpos.row, + curpos.column + imageData.name.length + 2 + ); + } + }; + // win/linux: Ctrl+M, mac: Command+M + var markdownToMention = function (editor) { + var originalRange = editor.getSelectionRange(); + if (editor.selection.isEmpty()) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' @[]'); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + 3); + } else { + var range = editor.getSelectionRange(); + var text = editor.session.getTextRange(range); + editor.session.replace(range, '@[' + text + ']'); + editor.focus(); + editor.selection.moveTo( + originalRange.end.row, + originalRange.end.column + 3 + ) + } + }; + // Insert Emoji to text editor: $('.insert-emoji').data('emoji-target') + var markdownToEmoji = function (editor, data_target) { + var curpos = editor.getCursorPosition(); + editor.session.insert(curpos, ' ' + data_target + ' '); + editor.focus(); + editor.selection.moveTo(curpos.row, curpos.column + data_target.length + 2); + }; + // Markdown Image Uploader auto insert to editor. + // with special insert, eg: ![avatar.png](i.imgur.com/DytfpTz.png) + var markdownToUploadImage = function (editor) { + var firstForm = $('#' + editorId).closest('form').get(0); + var field_name = editor.container.id.replace('martor-', ''); + var form = new FormData(firstForm); + form.append('csrfmiddlewaretoken', getCookie('csrftoken')); + + $.ajax({ + url: textareaId.data('upload-url'), + type: 'POST', + data: form, + async: true, + cache: false, + contentType: false, + enctype: 'multipart/form-data', + processData: false, + beforeSend: function () { + console.log('Uploading...'); + $('.upload-progress[data-field-name=' + field_name + ']').show(); + }, + success: function (response) { + $('.upload-progress[data-field-name=' + field_name + ']').hide(); + if (response.status == 200) { + console.log(response); + markdownToImageLink( + editor = editor, + imageData = { name: response.name, link: response.link } + ); + } else { + alert(response.error); + } + }, + error: function (response) { + console.log("error", response); + $('.upload-progress[data-field-name=' + field_name + ']').hide(); + } + }); + return false; + }; + + // Trigger Click + $('.markdown-bold[data-field-name=' + field_name + ']').click(function () { + markdownToBold(editor); + }); + $('.markdown-italic[data-field-name=' + field_name + ']').click(function () { + markdownToItalic(editor); + }); + $('.markdown-horizontal[data-field-name=' + field_name + ']').click(function () { + markdownToHorizontal(editor); + }); + $('.markdown-h1[data-field-name=' + field_name + ']').click(function () { + markdownToH1(editor); + }); + $('.markdown-h2[data-field-name=' + field_name + ']').click(function () { + markdownToH2(editor); + }); + $('.markdown-h3[data-field-name=' + field_name + ']').click(function () { + markdownToH3(editor); + }); + $('.markdown-pre[data-field-name=' + field_name + ']').click(function () { + markdownToPre(editor); + }); + $('.markdown-code[data-field-name=' + field_name + ']').click(function () { + markdownToCode(editor); + }); + $('.markdown-blockquote[data-field-name=' + field_name + ']').click(function () { + markdownToBlockQuote(editor); + }); + $('.markdown-unordered-list[data-field-name=' + field_name + ']').click(function () { + markdownToUnorderedList(editor); + }); + $('.markdown-ordered-list[data-field-name=' + field_name + ']').click(function () { + markdownToOrderedList(editor); + }); + $('.markdown-link[data-field-name=' + field_name + ']').click(function () { + markdownToLink(editor); + }); + $('.markdown-image-link[data-field-name=' + field_name + ']').click(function () { + markdownToImageLink(editor); + }); + + // Custom decission for toolbar buttons. + var btnMention = $('.markdown-direct-mention[data-field-name=' + field_name + ']'); // To Direct Mention + var btnUpload = $('.markdown-image-upload[data-field-name=' + field_name + ']'); // To Upload Image + + if (editorConfig.mention == 'true') { + btnMention.click(function () { + markdownToMention(editor); + }); + } else { + btnMention.remove(); + // Disable help of `mention` + $('.markdown-reference tbody tr')[1].remove(); + } + + if (editorConfig.imgur == 'true') { + btnUpload.on('change', function (evt) { + evt.preventDefault(); + markdownToUploadImage(editor); + }); + } else { + btnUpload.remove(); + } + + // Trigger Keyboards + editor.commands.addCommand({ + name: 'markdownToBold', + bindKey: { win: 'Ctrl-B', mac: 'Command-B' }, + exec: function (editor) { + markdownToBold(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToItalic', + bindKey: { win: 'Ctrl-I', mac: 'Command-I' }, + exec: function (editor) { + markdownToItalic(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToUnderscores', + bindKey: { win: 'Ctrl-Shift-U', mac: 'Command-Shift-U' }, + exec: function (editor) { + markdownToUnderscores(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToStrikethrough', + bindKey: { win: 'Ctrl-Shift-S', mac: 'Command-Shift-S' }, + exec: function (editor) { + markdownToStrikethrough(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToHorizontal', + bindKey: { win: 'Ctrl-H', mac: 'Command-H' }, + exec: function (editor) { + markdownToHorizontal(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToH1', + bindKey: { win: 'Ctrl-Alt-1', mac: 'Command-Option-1' }, + exec: function (editor) { + markdownToH1(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToH2', + bindKey: { win: 'Ctrl-Alt-2', mac: 'Command-Option-3' }, + exec: function (editor) { + markdownToH2(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToH3', + bindKey: { win: 'Ctrl-Alt-3', mac: 'Command-Option-3' }, + exec: function (editor) { + markdownToH3(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToPre', + bindKey: { win: 'Ctrl-Alt-P', mac: 'Command-Option-P' }, + exec: function (editor) { + markdownToPre(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToCode', + bindKey: { win: 'Ctrl-Alt-C', mac: 'Command-Option-C' }, + exec: function (editor) { + markdownToCode(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToBlockQuote', + bindKey: { win: 'Ctrl-Q', mac: 'Command-Shift-K' }, + exec: function (editor) { + markdownToBlockQuote(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToUnorderedList', + bindKey: { win: 'Ctrl-U', mac: 'Command-U' }, + exec: function (editor) { + markdownToUnorderedList(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToOrderedList', + bindKey: { win: 'Ctrl-Shift+O', mac: 'Command-Option-O' }, + exec: function (editor) { + markdownToOrderedList(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToLink', + bindKey: { win: 'Ctrl-L', mac: 'Command-L' }, + exec: function (editor) { + markdownToLink(editor); + }, + readOnly: true + }); + editor.commands.addCommand({ + name: 'markdownToImageLink', + bindKey: { win: 'Ctrl-Shift-I', mac: 'Command-Option-I' }, + exec: function (editor) { + markdownToImageLink(editor); + }, + readOnly: true + }); + if (editorConfig.mention === 'true') { + editor.commands.addCommand({ + name: 'markdownToMention', + bindKey: { win: 'Ctrl-M', mac: 'Command-M' }, + exec: function (editor) { + markdownToMention(editor); + }, + readOnly: true + }); + } + }); + }; + + $(function () { + $('.main-martor').martor(); + }); + + if ('django' in window && 'jQuery' in window.django) + django.jQuery(document).on('formset:added', function (event) { + // add delay for formset to load + setTimeout(function(){ + var row = $(event.target); + row.find('.main-martor').each(function () { + var id = row.attr('id'); + id = id.substr(id.lastIndexOf('-') + 1); + // Notice here we are using our jQuery instead of Django's. + // This is because plugins are only loaded for ours. + var fixed = $(this.outerHTML.replace(/__prefix__/g, id)); + $(this).replaceWith(fixed); + fixed.martor(); + }); + }, 1000); + }); +})(jQuery); diff --git a/martor/static/martor/js/martor.tailwind.min.js b/martor/static/martor/js/martor.tailwind.min.js new file mode 100644 index 00000000..85cc1516 --- /dev/null +++ b/martor/static/martor/js/martor.tailwind.min.js @@ -0,0 +1,9 @@ +/** + * Name : Martor v1.6.45 + * Created by : Agus Makmun (Summon Agus) + * Release date : 15-Nov-2024 + * License : GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 + * Repository : https://github.com/agusmakmun/django-markdown-editor + * JS Minifier : https://jscompress.com +**/ +!function(D){(D=D||django.jQuery).fn.martor=function(){D(".martor").trigger("martor.init");function K(e){var n=null,o=0;if(document.cookie&&""!==document.cookie)for(var t=document.cookie.split(";");oNothing to preview

')},error:function(e){console.log("error",e)}})};let g;if(window.onload=function(){u()},"true"!==a.living?n.click(function(){m.hide(),u()}):c.on("change",function(){g&&clearTimeout(g),g=setTimeout(u,r.data("save-timeout"))}),"true"==a.spellcheck)try{enable_spellcheck(t)}catch(e){console.log("Spellcheck lib doesn't installed.")}"true"==a.emoji&&(langTools=ace.require("ace/ext/language_tools"),langTools.addCompleter(i)),"true"==a.mention&&(langTools=ace.require("ace/ext/language_tools"),langTools.addCompleter(s)),D(".martor-toolbar .dropdown").each(function(){var n=D(this),e=n.find(".dropdown-toggle"),o=n.find(".dropdown-menu");e.click(function(e){e.preventDefault(),e.stopPropagation(),D(".martor-toolbar .dropdown-menu").not(o).addClass("hidden"),o.toggleClass("hidden")}),D(document).click(function(e){n.is(e.target)||0!==n.has(e.target).length||o.addClass("hidden")})}),D(".markdown-help[data-field-name="+l+"]").click(function(e){e.preventDefault(),console.log("Help button clicked for field:",l);var n=".modal-help-guide[data-field-name="+l+"]";console.log("Modal selector:",n);e=D(n);console.log("Modal found:",e.length),H(n)}),D(".modal-help-guide[data-field-name="+l+"] .modal-close").click(function(e){e.preventDefault(),z(".modal-help-guide[data-field-name="+l+"]")}),D(".modal-help-guide[data-field-name="+l+"]").click(function(e){e.target!==this&&!D(e.target).hasClass("bg-gray-500")||z(".modal-help-guide[data-field-name="+l+"]")});function f(){D(document.body).removeClass("overflow-hidden"),D(this).attr({title:"Full Screen"}),D(this).find("svg.bi-arrows-angle-expand").removeClass("hidden"),D(this).find("svg.bi-arrows-angle-contract").addClass("hidden"),D(".main-martor-fullscreen").find(".martor-preview").removeAttr("style"),o.removeClass("main-martor-fullscreen"),p.removeAttr("style"),c.resize()}var p=D(".martor-field-"+l),v=D(".markdown-toggle-maximize[data-field-name="+l+"]");v.on("click",function(){!function(e){e.attr({title:"Minimize"}),e.find("svg.bi-arrows-angle-expand").addClass("hidden"),e.find("svg.bi-arrows-angle-contract").removeClass("hidden"),o.addClass("main-martor-fullscreen");var n=document.body.clientHeight-90;p.attr({style:"height:"+n+"px"}),D(".main-martor-fullscreen").find(".martor-preview").attr({style:"overflow-y: auto;height:"+n+"px"}),c.resize(),e.one("click",f),D(document.body).addClass("overflow-hidden")}(D(this))}),D(document).keyup(function(e){27==e.keyCode&&o.hasClass("main-martor-fullscreen")&&v.trigger("click")}),D(".markdown-emoji[data-field-name="+l+"]").click(function(){var t=D(".modal-emoji[data-field-name="+l+"]");H(t);var a="undefined"!=typeof emojis?emojis:[],i=t.find("#emoji-grid-"+l),s=t.find(".emoji-loader-init");0===i.children().length?(A(s),setTimeout(function(){try{console.log("Loading emojis, count:",a.length);for(var e=0;e'+n+'");i.append(n)}t.find(".insert-emoji").off("click").on("click",function(){var e=D(this).data("emoji-target");console.log("Emoji selected:",e),M(c,e),z(t)}),_(s),console.log("Emojis loaded successfully")}catch(e){console.error("Error loading emojis:",e),_(s),i.html('
Error loading emojis. Please try again.
')}},100)):console.log("Emojis already loaded")}),D(".modal-emoji[data-field-name="+l+"] .modal-close").click(function(){z(".modal-emoji[data-field-name="+l+"]")}),D(".modal-emoji[data-field-name="+l+"] .insert-emoji-popular").click(function(){var e=D(this).data("emoji-target");M(c,e),z(".modal-emoji[data-field-name="+l+"]")}),c.setValue(r.val(),-1);function h(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o," **** "),e.focus(),e.selection.moveTo(o.row,o.column+3)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"**"+o+"**"),t.end.column+=4,e.focus(),e.selection.setSelectionRange(t))}function w(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o," __ "),e.focus(),e.selection.moveTo(o.row,o.column+2)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"_"+o+"_"),t.end.column+=2,e.focus(),e.selection.setSelectionRange(t))}function y(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n----------\n\n"),e.focus(),e.selection.moveTo(o.row+4,o.column+10)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n----------\n\n"+o),e.focus(),e.selection.moveTo(t.end.row+4,t.end.column+10))}function C(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n# "),e.focus(),e.selection.moveTo(o.row+2,o.column+2)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n# "+o+"\n"),e.focus(),e.selection.moveTo(t.end.row+2,t.end.column+2))}function k(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n## "),e.focus(),e.selection.moveTo(o.row+2,o.column+3)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n## "+o+"\n"),e.focus(),e.selection.moveTo(t.end.row+2,t.end.column+3))}function b(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n### "),e.focus(),e.selection.moveTo(o.row+2,o.column+4)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n### "+o+"\n"),e.focus(),e.selection.moveTo(t.end.row+2,t.end.column+4))}function T(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n```\n\n```\n"),e.focus(),e.selection.moveTo(o.row+3,o.column)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n```\n"+o+"\n```\n"),e.focus(),e.selection.moveTo(t.end.row+3,t.end.column+3))}function x(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o," `` "),e.focus(),e.selection.moveTo(o.row,o.column+2)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"`"+o+"`"),t.end.column+=2,e.focus(),e.selection.setSelectionRange(t))}function S(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n> \n"),e.focus(),e.selection.moveTo(o.row+2,o.column+2)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n> "+o+"\n"),e.focus(),e.selection.moveTo(t.end.row+2,t.end.column+2))}function R(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n* "),e.focus(),e.selection.moveTo(o.row+2,o.column+2)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n* "+o),e.focus(),e.selection.moveTo(t.end.row+2,t.end.column+2))}function j(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o,"\n\n1. "),e.focus(),e.selection.moveTo(o.row+2,o.column+3)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"\n\n1. "+o),e.focus(),e.selection.moveTo(t.end.row+2,t.end.column+3))}function O(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o," [](https://) "),e.focus(),e.selection.moveTo(o.row,o.column+2)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"["+o+"](https://) "),e.focus(),e.selection.moveTo(t.end.row,t.end.column+11))}function P(e,n){var o,t,a,i=e.getSelectionRange();void 0===n?e.selection.isEmpty()?(a=e.getCursorPosition(),e.session.insert(a," ![](https://) "),e.focus(),e.selection.moveTo(a.row,a.column+3)):(o=e.getSelectionRange(),t=e.session.getTextRange(o),e.session.replace(o,"!["+t+"](https://) "),e.focus(),e.selection.moveTo(i.end.row,i.end.column+12)):(a=e.getCursorPosition(),e.session.insert(a,"!["+n.name+"]("+n.link+") "),e.focus(),e.selection.moveTo(a.row,a.column+n.name.length+2))}function E(e){var n,o,t=e.getSelectionRange();e.selection.isEmpty()?(o=e.getCursorPosition(),e.session.insert(o," @[]"),e.focus(),e.selection.moveTo(o.row,o.column+3)):(n=e.getSelectionRange(),o=e.session.getTextRange(n),e.session.replace(n,"@["+o+"]"),e.focus(),e.selection.moveTo(t.end.row,t.end.column+3))}var M=function(e,n){var o=e.getCursorPosition();e.session.insert(o," "+n+" "),e.focus(),e.selection.moveTo(o.row,o.column+n.length+2)};D(".markdown-bold[data-field-name="+l+"]").click(function(){h(c)}),D(".markdown-italic[data-field-name="+l+"]").click(function(){w(c)}),D(".markdown-horizontal[data-field-name="+l+"]").click(function(){y(c)}),D(".markdown-h1[data-field-name="+l+"]").click(function(){C(c)}),D(".markdown-h2[data-field-name="+l+"]").click(function(){k(c)}),D(".markdown-h3[data-field-name="+l+"]").click(function(){b(c)}),D(".markdown-pre[data-field-name="+l+"]").click(function(){T(c)}),D(".markdown-code[data-field-name="+l+"]").click(function(){x(c)}),D(".markdown-blockquote[data-field-name="+l+"]").click(function(){S(c)}),D(".markdown-unordered-list[data-field-name="+l+"]").click(function(){R(c)}),D(".markdown-ordered-list[data-field-name="+l+"]").click(function(){j(c)}),D(".markdown-link[data-field-name="+l+"]").click(function(){O(c)}),D(".markdown-image-link[data-field-name="+l+"]").click(function(){P(c)});i=D(".markdown-direct-mention[data-field-name="+l+"]"),s=D(".markdown-image-upload[data-field-name="+l+"]");"true"==a.mention?i.click(function(){E(c)}):(i.remove(),D(".markdown-reference tbody tr")[1].remove()),"true"==a.imgur?s.on("change",function(e){var n,o;e.preventDefault(),n=c,e=D("#"+t).closest("form").get(0),o=n.container.id.replace("martor-",""),(e=new FormData(e)).append("csrfmiddlewaretoken",K("csrftoken")),D.ajax({url:r.data("upload-url"),type:"POST",data:e,async:!0,cache:!1,contentType:!1,enctype:"multipart/form-data",processData:!1,beforeSend:function(){console.log("Uploading..."),D(".upload-progress[data-field-name="+o+"]").show()},success:function(e){D(".upload-progress[data-field-name="+o+"]").hide(),200==e.status?(console.log(e),P(n,imageData={name:e.name,link:e.link})):alert(e.error)},error:function(e){console.log("error",e),D(".upload-progress[data-field-name="+o+"]").hide()}})}):s.remove(),c.commands.addCommand({name:"markdownToBold",bindKey:{win:"Ctrl-B",mac:"Command-B"},exec:function(e){h(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToItalic",bindKey:{win:"Ctrl-I",mac:"Command-I"},exec:function(e){w(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToUnderscores",bindKey:{win:"Ctrl-Shift-U",mac:"Command-Shift-U"},exec:function(e){var n,o,t;t=(n=e).getSelectionRange(),n.selection.isEmpty()?(o=n.getCursorPosition(),n.session.insert(o," ++++ "),n.focus(),n.selection.moveTo(o.row,o.column+3)):(e=n.getSelectionRange(),o=n.session.getTextRange(e),n.session.replace(e,"++"+o+"++"),t.end.column+=4,n.focus(),n.selection.setSelectionRange(t))},readOnly:!0}),c.commands.addCommand({name:"markdownToStrikethrough",bindKey:{win:"Ctrl-Shift-S",mac:"Command-Shift-S"},exec:function(e){var n,o,t;t=(n=e).getSelectionRange(),n.selection.isEmpty()?(o=n.getCursorPosition(),n.session.insert(o," ~~~~ "),n.focus(),n.selection.moveTo(o.row,o.column+3)):(e=n.getSelectionRange(),o=n.session.getTextRange(e),n.session.replace(e,"~~"+o+"~~"),t.end.column+=4,n.focus(),n.selection.setSelectionRange(t))},readOnly:!0}),c.commands.addCommand({name:"markdownToHorizontal",bindKey:{win:"Ctrl-H",mac:"Command-H"},exec:function(e){y(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToH1",bindKey:{win:"Ctrl-Alt-1",mac:"Command-Option-1"},exec:function(e){C(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToH2",bindKey:{win:"Ctrl-Alt-2",mac:"Command-Option-3"},exec:function(e){k(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToH3",bindKey:{win:"Ctrl-Alt-3",mac:"Command-Option-3"},exec:function(e){b(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToPre",bindKey:{win:"Ctrl-Alt-P",mac:"Command-Option-P"},exec:function(e){T(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToCode",bindKey:{win:"Ctrl-Alt-C",mac:"Command-Option-C"},exec:function(e){x(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToBlockQuote",bindKey:{win:"Ctrl-Q",mac:"Command-Shift-K"},exec:function(e){S(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToUnorderedList",bindKey:{win:"Ctrl-U",mac:"Command-U"},exec:function(e){R(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToOrderedList",bindKey:{win:"Ctrl-Shift+O",mac:"Command-Option-O"},exec:function(e){j(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToLink",bindKey:{win:"Ctrl-L",mac:"Command-L"},exec:function(e){O(e)},readOnly:!0}),c.commands.addCommand({name:"markdownToImageLink",bindKey:{win:"Ctrl-Shift-I",mac:"Command-Option-I"},exec:function(e){P(e)},readOnly:!0}),"true"===a.mention&&c.commands.addCommand({name:"markdownToMention",bindKey:{win:"Ctrl-M",mac:"Command-M"},exec:function(e){E(e)},readOnly:!0})})},D(function(){D(".main-martor").martor()}),"django"in window&&"jQuery"in window.django&&django.jQuery(document).on("formset:added",function(e){setTimeout(function(){var n=D(e.target);n.find(".main-martor").each(function(){var e=(e=n.attr("id")).substr(e.lastIndexOf("-")+1),e=D(this.outerHTML.replace(/__prefix__/g,e));D(this).replaceWith(e),e.martor()})},1e3)})}(jQuery); diff --git a/martor/static/plugins/css/tailwind.min.css b/martor/static/plugins/css/tailwind.min.css new file mode 100644 index 00000000..9a318403 --- /dev/null +++ b/martor/static/plugins/css/tailwind.min.css @@ -0,0 +1,1289 @@ +/** + * Tailwind CSS Base Components for Martor + * This file provides minimal Tailwind CSS base styles for environments that don't have Tailwind CSS configured + * For production use, it's recommended to use a full Tailwind CSS build + */ + +/* Base Reset and Normalization */ +*, +::before, +::after { + box-sizing: border-box; + border-width: 0; + border-style: solid; + border-color: #e5e7eb +} + +::before, +::after { + --tw-content: '' +} + +html { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + -moz-tab-size: 4; + tab-size: 4; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" +} + +body { + margin: 0; + line-height: inherit +} + +hr { + height: 0; + color: inherit; + border-top-width: 1px +} + +abbr:where([title]) { + text-decoration: underline dotted +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit +} + +a { + color: inherit; + text-decoration: inherit +} + +b, +strong { + font-weight: bolder +} + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace; + font-size: 1em +} + +small { + font-size: 80% +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sub { + bottom: -.25em +} + +sup { + top: -.5em +} + +table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + font-weight: inherit; + line-height: inherit; + color: inherit; + margin: 0; + padding: 0 +} + +button, +select { + text-transform: none +} + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + background-color: transparent; + background-image: none +} + +:-moz-focusring { + outline: auto +} + +:-moz-ui-invalid { + box-shadow: none +} + +progress { + vertical-align: baseline +} + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto +} + +[type='search'] { + -webkit-appearance: textfield; + outline-offset: -2px +} + +::-webkit-search-decoration { + -webkit-appearance: none +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit +} + +summary { + display: list-item +} + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0 +} + +fieldset { + margin: 0; + padding: 0 +} + +legend { + padding: 0 +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0 +} + +textarea { + resize: vertical +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + color: #9ca3af +} + +button, +[role="button"] { + cursor: pointer +} + +:disabled { + cursor: default +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle +} + +img, +video { + max-width: 100%; + height: auto +} + +/* Utility Classes for Martor */ +.hidden { + display: none +} + +.block { + display: block +} + +.inline-block { + display: inline-block +} + +.flex { + display: flex +} + +.inline-flex { + display: inline-flex +} + +.grid { + display: grid +} + +.absolute { + position: absolute +} + +.relative { + position: relative +} + +.fixed { + position: fixed +} + +.inset-0 { + top: 0px; + right: 0px; + bottom: 0px; + left: 0px +} + +.left-0 { + left: 0px +} + +.right-0 { + right: 0px +} + +.top-0 { + top: 0px +} + +.bottom-0 { + bottom: 0px +} + +.-bottom-2 { + bottom: -0.5rem +} + +.right-2 { + right: 0.5rem +} + +.z-50 { + z-index: 50 +} + +.z-\[100\] { + z-index: 100 +} + +.z-\[9999\] { + z-index: 9999 +} + +.m-0 { + margin: 0 +} + +.mx-auto { + margin-left: auto; + margin-right: auto +} + +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem +} + +.my-5 { + margin-top: 1.25rem; + margin-bottom: 1.25rem +} + +.mb-2 { + margin-bottom: 0.5rem +} + +.mb-4 { + margin-bottom: 1rem +} + +.mb-5 { + margin-bottom: 1.25rem +} + +.ml-2 { + margin-left: 0.5rem +} + +.ml-3 { + margin-left: 0.75rem +} + +.mr-2 { + margin-right: 0.5rem +} + +.mr-3 { + margin-right: 0.75rem +} + +.mt-0 { + margin-top: 0px +} + +.mt-4 { + margin-top: 1rem +} + +.-ml-6 { + margin-left: -1.5rem +} + +.-ml-8 { + margin-left: -2rem +} + +.h-1 { + height: 0.25rem +} + +.h-1\.5 { + height: 0.375rem +} + +.h-4 { + height: 1rem +} + +.h-5 { + height: 1rem +} + +.h-6 { + height: 1.5rem +} + +.h-8 { + height: 2rem +} + +.h-10 { + height: 2.5rem +} + +.h-12 { + height: 3rem +} + +.h-64 { + height: 16rem +} + +.h-\[90\%\] { + height: 90% +} + +.h-\[500px\] { + height: 500px +} + +.h-full { + height: 100% +} + +.max-h-\[500px\] { + max-height: 500px +} + +.max-h-64 { + max-height: 16rem +} + +.max-h-96 { + max-height: 24rem +} + +.max-h-full { + max-height: 100% +} + +.min-h-\[100px\] { + min-height: 100px +} + +.min-h-64 { + min-height: 16rem +} + +.min-h-screen { + min-height: 100vh +} + +.w-1\.5 { + width: 0.375rem +} + +.w-3 { + width: 0.75rem +} + +.w-4 { + width: 1rem +} + +.w-5 { + width: 1.25rem +} + +.w-6 { + width: 1.5rem +} + +.w-8 { + width: 2rem +} + +.w-10 { + width: 2.5rem +} + +.w-12 { + width: 3rem +} + +.w-48 { + width: 12rem +} + +.w-auto { + width: auto +} + +.w-full { + width: 100% +} + +.max-w-2xl { + max-width: 42rem +} + +.max-w-4xl { + max-width: 56rem +} + +.max-w-5 { + max-width: 1.25rem +} + +.max-w-full { + max-width: 100% +} + +.flex-shrink-0 { + flex-shrink: 0 +} + +.resize-y { + resize: vertical +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)) +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)) +} + +.grid-cols-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)) +} + +.flex-col { + flex-direction: column +} + +.flex-wrap { + flex-wrap: wrap +} + +.items-center { + align-items: center +} + +.items-end { + align-items: flex-end +} + +.items-start { + align-items: flex-start +} + +.justify-center { + justify-content: center +} + +.justify-between { + justify-content: space-between +} + +.gap-1 { + gap: 0.25rem +} + +.gap-2 { + gap: 0.5rem +} + +.gap-4 { + gap: 1rem +} + +.gap-6 { + gap: 1.5rem +} + +.space-x-1>:not([hidden])~:not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.25rem*var(--tw-space-x-reverse)); + margin-left: calc(0.25rem*calc(1-var(--tw-space-x-reverse))) +} + +.space-x-8>:not([hidden])~:not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2rem*var(--tw-space-x-reverse)); + margin-left: calc(2rem*calc(1-var(--tw-space-x-reverse))) +} + +.space-y-1>:not([hidden])~:not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem*calc(1-var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem*var(--tw-space-y-reverse)) +} + +.space-y-2>:not([hidden])~:not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem*calc(1-var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem*var(--tw-space-y-reverse)) +} + +.overflow-auto { + overflow: auto +} + +.overflow-hidden { + overflow: hidden +} + +.overflow-y-auto { + overflow-y: auto +} + +.whitespace-nowrap { + white-space: nowrap +} + +.whitespace-pre { + white-space: pre +} + +.rounded { + border-radius: 0.25rem +} + +.rounded-full { + border-radius: 9999px +} + +.rounded-lg { + border-radius: 0.5rem +} + +.rounded-md { + border-radius: 0.375rem +} + +.border { + border-width: 1px +} + +.border-b { + border-bottom-width: 1px +} + +.border-b-2 { + border-bottom-width: 2px +} + +.border-l { + border-left-width: 1px +} + +.border-l-4 { + border-left-width: 4px +} + +.border-r { + border-right-width: 1px +} + +.border-t { + border-top-width: 1px +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235/var(--tw-border-opacity)) +} + +.border-gray-300 { + --tw-border-opacity: 1; + border-color: rgb(209 213 219/var(--tw-border-opacity)) +} + +.border-gray-800 { + --tw-border-opacity: 1; + border-color: rgb(31 41 55/var(--tw-border-opacity)) +} + +.border-transparent { + border-color: transparent +} + +.border-0 { + border-width: 0px +} + +.bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0/var(--tw-bg-opacity)) +} + +.bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254/var(--tw-bg-opacity)) +} + +.bg-blue-600 { + --tw-bg-opacity: 1; + background-color: rgb(37 99 235/var(--tw-bg-opacity)) +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251/var(--tw-bg-opacity)) +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246/var(--tw-bg-opacity)) +} + +.bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128/var(--tw-bg-opacity)) +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255/var(--tw-bg-opacity)) +} + +.bg-yellow-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195/var(--tw-bg-opacity)) +} + +.bg-opacity-75 { + --tw-bg-opacity: 0.75 +} + +.bg-opacity-85 { + --tw-bg-opacity: 0.85 +} + +.bg-transparent { + background-color: transparent +} + +.p-0 { + padding: 0px +} + +.p-1 { + padding: 0.25rem +} + +.p-2 { + padding: 0.5rem +} + +.p-4 { + padding: 1rem +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem +} + +.py-1 { + padding-top: 0.25rem; + padding-bottom: 0.25rem +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem +} + +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem +} + +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem +} + +.pb-0 { + padding-bottom: 0px +} + +.pb-20 { + padding-bottom: 5rem +} + +.pl-4 { + padding-left: 1rem +} + +.pl-8 { + padding-left: 2rem +} + +.pl-9 { + padding-left: 2.25rem +} + +.pl-10 { + padding-left: 2.5rem +} + +.pr-3 { + padding-right: 0.75rem +} + +.pt-1 { + padding-top: 0.25rem +} + +.pt-4 { + padding-top: 1rem +} + +.pt-5 { + padding-top: 1.25rem +} + +.pt-16 { + padding-top: 4rem +} + +.pt-20 { + padding-top: 5rem +} + +.text-left { + text-align: left +} + +.text-center { + text-align: center +} + +.text-right { + text-align: right +} + +.align-bottom { + vertical-align: bottom +} + +.align-middle { + vertical-align: middle +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem +} + +.font-bold { + font-weight: 700 +} + +.font-medium { + font-weight: 500 +} + +.font-semibold { + font-weight: 600 +} + +.leading-5 { + line-height: 1.25rem +} + +.leading-6 { + line-height: 1.5rem +} + +.leading-none { + line-height: 1 +} + +.leading-normal { + line-height: 1.5 +} + +.leading-relaxed { + line-height: 1.625 +} + +.list-disc { + list-style-type: disc +} + +.list-none { + list-style-type: none +} + +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0/var(--tw-text-opacity)) +} + +.text-blue-600 { + --tw-text-opacity: 1; + color: rgb(37 99 235/var(--tw-text-opacity)) +} + +.text-gray-300 { + --tw-text-opacity: 1; + color: rgb(209 213 219/var(--tw-text-opacity)) +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175/var(--tw-text-opacity)) +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128/var(--tw-text-opacity)) +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99/var(--tw-text-opacity)) +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81/var(--tw-text-opacity)) +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55/var(--tw-text-opacity)) +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39/var(--tw-text-opacity)) +} + +.text-red-600 { + --tw-text-opacity: 1; + color: rgb(220 38 38/var(--tw-text-opacity)) +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255/var(--tw-text-opacity)) +} + +.text-yellow-600 { + --tw-text-opacity: 1; + color: rgb(217 119 6/var(--tw-text-opacity)) +} + +.no-underline { + text-decoration-line: none +} + +.placeholder-gray-400::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(156 163 175/var(--tw-placeholder-opacity)) +} + +.placeholder-gray-500::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(107 114 128/var(--tw-placeholder-opacity)) +} + +.opacity-0 { + opacity: 0 +} + +.opacity-100 { + opacity: 1 +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0/0.1), 0 4px 6px -4px rgb(0 0 0/0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)) +} + +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0/0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)) +} + +.shadow-xl { + --tw-shadow: 0 20px 25px -5px rgb(0 0 0/0.1), 0 8px 10px -6px rgb(0 0 0/0.1); + --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)) +} + +.outline-none { + outline: 2px solid transparent; + outline-offset: 2px +} + +.cursor-pointer { + cursor: pointer +} + +.clear-both { + clear: both +} + +.float-left { + float: left +} + +.float-right { + float: right +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)) +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms +} + +.transition-opacity { + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms +} + +/* Animation classes */ +@keyframes spin { + to { + transform: rotate(360deg) + } +} + +.animate-spin { + animation: spin 1s linear infinite +} + +/* Hover states */ +.hover\:bg-blue-700:hover { + --tw-bg-opacity: 1; + background-color: rgb(29 78 216/var(--tw-bg-opacity)) +} + +.hover\:bg-gray-50:hover { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251/var(--tw-bg-opacity)) +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246/var(--tw-bg-opacity)) +} + +.hover\:text-gray-600:hover { + --tw-text-opacity: 1; + color: rgb(75 85 99/var(--tw-text-opacity)) +} + +.hover\:text-gray-700:hover { + --tw-text-opacity: 1; + color: rgb(55 65 81/var(--tw-text-opacity)) +} + +.hover\:text-gray-900:hover { + --tw-text-opacity: 1; + color: rgb(17 24 39/var(--tw-text-opacity)) +} + +.hover\:border-gray-300:hover { + --tw-border-opacity: 1; + border-color: rgb(209 213 219/var(--tw-border-opacity)) +} + +/* Focus states */ +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px +} + +.focus\:ring-1:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) +} + +.focus\:ring-2:focus { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) +} + +.focus\:ring-blue-500:focus { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(59 130 246/var(--tw-ring-opacity)) +} + +.focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px +} + +.focus\:border-blue-500:focus { + --tw-border-opacity: 1; + border-color: rgb(59 130 246/var(--tw-border-opacity)) +} + +.focus\:text-gray-600:focus { + --tw-text-opacity: 1; + color: rgb(75 85 99/var(--tw-text-opacity)) +} + +.focus\:text-gray-700:focus { + --tw-text-opacity: 1; + color: rgb(55 65 81/var(--tw-text-opacity)) +} + +.focus\:text-gray-900:focus { + --tw-text-opacity: 1; + color: rgb(17 24 39/var(--tw-text-opacity)) +} + +.focus\:bg-gray-100:focus { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246/var(--tw-bg-opacity)) +} + +.focus\:placeholder-gray-400:focus::placeholder { + --tw-placeholder-opacity: 1; + color: rgb(156 163 175/var(--tw-placeholder-opacity)) +} + +/* Focus within states */ +.focus-within\:ring-2:focus-within { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000) +} + +.focus-within\:ring-blue-500:focus-within { + --tw-ring-opacity: 1; + --tw-ring-color: rgb(59 130 246/var(--tw-ring-opacity)) +} + +/* Responsive design */ +@media (min-width:640px) { + .sm\:block { + display: block + } + + .sm\:inline-block { + display: inline-block + } + + .sm\:flex { + display: flex + } + + .sm\:align-middle { + vertical-align: middle + } + + .sm\:h-screen { + height: 100vh + } + + .sm\:h-10 { + height: 2.5rem + } + + .sm\:w-10 { + width: 2.5rem + } + + .sm\:w-auto { + width: auto + } + + .sm\:max-w-2xl { + max-width: 42rem + } + + .sm\:max-w-4xl { + max-width: 56rem + } + + .sm\:flex-row-reverse { + flex-direction: row-reverse + } + + .sm\:p-0 { + padding: 0px + } + + .sm\:px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem + } + + .sm\:pb-4 { + padding-bottom: 1rem + } + + .sm\:text-left { + text-align: left + } + + .sm\:text-sm { + font-size: 0.875rem; + line-height: 1.25rem + } + + .sm\:mx-0 { + margin-left: 0px; + margin-right: 0px + } + + .sm\:ml-3 { + margin-left: 0.75rem + } + + .sm\:ml-4 { + margin-left: 1rem + } + + .sm\:mt-0 { + margin-top: 0px + } + + .sm\:my-8 { + margin-top: 2rem; + margin-bottom: 2rem + } +} + +@media (min-width:768px) { + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)) + } + + .md\:col-span-2 { + grid-column: span 2/span 2 + } +} diff --git a/martor/templates/martor/tailwind/editor.html b/martor/templates/martor/tailwind/editor.html new file mode 100644 index 00000000..3162a92d --- /dev/null +++ b/martor/templates/martor/tailwind/editor.html @@ -0,0 +1,57 @@ +{% load i18n %} +
+
+ + +
+
+ +
+ {{ martor }} +
+ + +
+
+ + {% include 'martor/tailwind/guide.html' %} + {% include 'martor/tailwind/emoji.html' %} +
diff --git a/martor/templates/martor/tailwind/emoji.html b/martor/templates/martor/tailwind/emoji.html new file mode 100644 index 00000000..3baf1503 --- /dev/null +++ b/martor/templates/martor/tailwind/emoji.html @@ -0,0 +1,131 @@ +{% load i18n %} + + + + + diff --git a/martor/templates/martor/tailwind/guide.html b/martor/templates/martor/tailwind/guide.html new file mode 100644 index 00000000..88754555 --- /dev/null +++ b/martor/templates/martor/tailwind/guide.html @@ -0,0 +1,195 @@ +{% load i18n static %} + + + diff --git a/martor/templates/martor/tailwind/toolbar.html b/martor/templates/martor/tailwind/toolbar.html new file mode 100644 index 00000000..7f44734b --- /dev/null +++ b/martor/templates/martor/tailwind/toolbar.html @@ -0,0 +1,151 @@ +{% load i18n %} + + diff --git a/martor/widgets.py b/martor/widgets.py index 6b8c076f..dcfd16eb 100644 --- a/martor/widgets.py +++ b/martor/widgets.py @@ -23,7 +23,7 @@ def get_theme(): """function to get the selected theme""" - supported_themes = ["bootstrap", "semantic"] + supported_themes = ["bootstrap", "semantic", "tailwind"] if MARTOR_THEME in supported_themes: return MARTOR_THEME return "bootstrap" diff --git a/martor_demo/app/templates/tailwind/base.html b/martor_demo/app/templates/tailwind/base.html new file mode 100644 index 00000000..afe30735 --- /dev/null +++ b/martor_demo/app/templates/tailwind/base.html @@ -0,0 +1,104 @@ + + + + + + {% block title %}Martor Demo - Tailwind CSS{% endblock %} + + + + + + {% load static %} + {% block css %} + + + + {% endblock %} + + + + + +
+
+
+
+

Martor Demo

+ + Tailwind CSS + +
+ +
+
+
+ + +
+ {% block content %} + {% endblock %} +
+ + +
+
+
+

+ Martor Demo with Tailwind CSS Theme +

+

+ Built with Django and Tailwind CSS +

+
+
+
+ + + {% block js %} + + + + + + + + + + + {% endblock %} + + diff --git a/martor_demo/app/templates/tailwind/form.html b/martor_demo/app/templates/tailwind/form.html new file mode 100644 index 00000000..ff05db5d --- /dev/null +++ b/martor_demo/app/templates/tailwind/form.html @@ -0,0 +1,201 @@ +{% extends 'tailwind/base.html' %} +{% load static %} +{% block title %}Simple Form - Martor Demo (Tailwind){% endblock %} + +{% block content %} +
+ +
+
+

+ Martor Editor Demo +

+

+ A powerful Django Markdown Editor with Tailwind CSS styling +

+
+ + Live Preview + + + Ace Editor + + + Emoji Support + + + File Upload + +
+
+
+ + +
+
+ {% csrf_token %} + +
+ +
+ + {% if form.title.errors %} +
+ + + +
+ {% endif %} +
+ {% if form.title.errors %} +
+ {{ form.title.errors.0 }} +
+ {% endif %} + {% if form.title.help_text %} +
+ {{ form.title.help_text }} +
+ {% endif %} +
+ +
+ +
+ {{ form.description }} + {% if form.description.errors %} +
+ + + +
+ {% endif %} +
+ {% if form.description.errors %} +
+ {{ form.description.errors.0 }} +
+ {% endif %} + {% if form.description.help_text %} +
+ {{ form.description.help_text }} +
+ {% endif %} +
+ +
+
+
+ + + + Use the toolbar for formatting options +
+
+
+ +
+
+
+
+ +
+

Features Available

+
+
+
+
+ + + +
+
+
+

Live Preview

+

Real-time markdown preview

+
+
+
+
+
+ + + +
+
+
+

Image Upload

+

Drag & drop image support

+
+
+
+
+
+ + + +
+
+
+

Emoji Support

+

Built-in emoji picker

+
+
+
+
+
+ + + +
+
+
+

Syntax Highlighting

+

Code block highlighting

+
+
+
+
+
+ + + +
+
+
+

User Mentions

+

@username support

+
+
+
+
+
+ + + +
+
+
+

Multiple Formats

+

Tables, lists, links & more

+
+
+
+
+
+{% endblock %} diff --git a/martor_demo/app/templates/tailwind/test_markdownify.html b/martor_demo/app/templates/tailwind/test_markdownify.html new file mode 100644 index 00000000..c1bd23d5 --- /dev/null +++ b/martor_demo/app/templates/tailwind/test_markdownify.html @@ -0,0 +1,251 @@ +{% extends 'tailwind/base.html' %} +{% load static %} +{% load martortags %} + +{% block title %}Test Markdownify - Martor Demo (Tailwind){% endblock %} + +{% block content %} +
+ +
+
+

+ Markdownify Test +

+

+ See how Martor renders Markdown content with Tailwind CSS styling +

+
+
+ + +
+ +
+

+ + + + Markdown Source +

+
+
{{ content }}
+
+
+ + +
+

+ + + + + Rendered Output +

+
+ {{ content|safe_markdown }} +
+
+
+ + +
+

Sample Markdown Examples

+ +
+ +
+

Headers

+
+
+ +# Heading 1
+## Heading 2
+### Heading 3 +
+
+
+

Heading 1

+

Heading 2

+

Heading 3

+
+
+
+ + +
+

Text Formatting

+
+
+ +**Bold text**
+*Italic text*
+~~Strikethrough~~
+++Underline++
+`inline code` +
+
+
+

Bold text

+

Italic text

+

Strikethrough

+

Underline

+

inline code

+
+
+
+ + +
+

Lists

+
+
+ +* Unordered item 1
+* Unordered item 2
+
+1. Ordered item 1
+2. Ordered item 2 +
+
+
+
    +
  • Unordered item 1
  • +
  • Unordered item 2
  • +
+
    +
  1. Ordered item 1
  2. +
  3. Ordered item 2
  4. +
+
+
+
+ + +
+

Links & Images

+
+
+ +[Link text](https://example.com)
+![Alt text](https://via.placeholder.com/150) +
+
+
+

Link text

+ Alt text +
+
+
+ + +
+

Code Blocks

+
+
+ +```python
+def hello_world():
+    print("Hello, World!")
+``` +
+
+
+
def hello_world():
+    print("Hello, World!")
+
+
+
+ + +
+

Tables

+
+
+ +| Name | Age | City |
+|------|-----|------|
+| John | 30 | NYC |
+| Jane | 25 | LA | +
+
+
+ + + + + + + + + + + + + + + + + + + + +
NameAgeCity
John30NYC
Jane25LA
+
+
+
+ + +
+

Blockquotes

+
+
+ +> This is a blockquote
+> with multiple lines
+> +> > Nested quote +
+
+
+
+ This is a blockquote
+ with multiple lines +
+ Nested quote +
+
+
+
+
+
+
+ + + +
+ + +{% endblock %} diff --git a/martor_demo/martor_demo/settings.py b/martor_demo/martor_demo/settings.py index c007808d..a3d1b5bf 100644 --- a/martor_demo/martor_demo/settings.py +++ b/martor_demo/martor_demo/settings.py @@ -30,8 +30,11 @@ DEBUG = True ALLOWED_HOSTS = ["*"] +# Required for Martor AJAX functionality +CSRF_COOKIE_HTTPONLY = False + # Martor Configuration -MARTOR_THEME = "bootstrap" # semantic +MARTOR_THEME = "tailwind" # bootstrap, semantic MARTOR_ENABLE_LABEL = True MARTOR_ENABLE_CONFIGS = { "emoji": "true", # to enable/disable emoji icons.