marksmith 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -3
  3. data/app/assets/images/marksmith/svgs/bold.svg +1 -1
  4. data/app/assets/images/marksmith/svgs/code.svg +1 -1
  5. data/app/assets/images/marksmith/svgs/color-swatch.svg +1 -1
  6. data/app/assets/images/marksmith/svgs/gallery.svg +1 -1
  7. data/app/assets/images/marksmith/svgs/header.svg +1 -1
  8. data/app/assets/images/marksmith/svgs/image.svg +1 -1
  9. data/app/assets/images/marksmith/svgs/italic.svg +1 -1
  10. data/app/assets/images/marksmith/svgs/link.svg +1 -1
  11. data/app/assets/images/marksmith/svgs/markdown.svg +1 -1
  12. data/app/assets/images/marksmith/svgs/ordered-list.svg +1 -1
  13. data/app/assets/images/marksmith/svgs/paperclip.svg +1 -1
  14. data/app/assets/images/marksmith/svgs/quote.svg +1 -1
  15. data/app/assets/images/marksmith/svgs/task-list.svg +1 -1
  16. data/app/assets/images/marksmith/svgs/unordered-list.svg +1 -1
  17. data/app/assets/javascripts/list_continuation_controller-full.esm.js +1 -1
  18. data/app/assets/javascripts/list_continuation_controller-no-stimulus.esm.js +1 -1
  19. data/app/assets/javascripts/marksmith_controller-full.esm.js +19 -12
  20. data/app/assets/javascripts/marksmith_controller-no-stimulus.esm.js +19 -12
  21. data/app/assets/stylesheets/marksmith.css +165 -29
  22. data/app/frontend/entrypoints/application.css +5 -0
  23. data/app/frontend/entrypoints/javascript/controllers/marksmith_controller.js +18 -11
  24. data/app/helpers/marksmith/marksmith_helper.rb +59 -0
  25. data/app/models/marksmith/editor.rb +17 -16
  26. data/app/models/marksmith/renderer.rb +8 -1
  27. data/app/views/marksmith/shared/_action_bar.html.erb +5 -2
  28. data/app/views/marksmith/shared/_editor.html.erb +3 -2
  29. data/app/views/marksmith/shared/_editor_pane.html.erb +13 -9
  30. data/app/views/marksmith/shared/_preview_pane.html.erb +2 -2
  31. data/app/views/marksmith/shared/_rendered_body.html.erb +1 -1
  32. data/app/views/marksmith/shared/_tabs.html.erb +4 -4
  33. data/app/views/marksmith/shared/_toolbar.html.erb +5 -2
  34. data/config/locales/marksmith.de.yml +17 -0
  35. data/lib/marksmith/engine.rb +2 -2
  36. data/lib/marksmith/version.rb +1 -1
  37. metadata +4 -5
  38. data/app/assets/images/marksmith/svgs/color-swatch copy.svg +0 -3
  39. data/app/assets/images/marksmith/svgs/link copy.svg +0 -3
  40. data/lib/marksmith/helper.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed572a170bb7408c863a6da5190ffd35ed7f9070c38e93f0337982bf24cd53c4
4
- data.tar.gz: c8c19b7705c18922362892e94e33300e18d0aa91467e1336496b5ac16168b492
3
+ metadata.gz: b9b7bca51b1e950acb74830923706300439557e2600e1ea147f3db026c3116e2
4
+ data.tar.gz: d442e841439c4873b688c1df818aada6d80087578b8564469395881652cd3e15
5
5
  SHA512:
6
- metadata.gz: c7e47d37276d05e9e908ecc54ef0a98f5fc07ba6e4c7f4847d1ef01658f806baae698db75e204959a2c846bbab59d9257f3f1715c9ef4bb748588f328ab67ab2
7
- data.tar.gz: e614a5b204d2c6587cf832426f67b95ebfcc4742ea5d757cc15c14f2ae08cea9fd6a4c23cccebc10faa8a6f02ad2d2a7c142a0d0e66762286e972457bde8e18d
6
+ metadata.gz: 43f2b59bfc02d83bc4d02ea6249c4f6d0a27ba5438be942669e6ab129cdaf40c86c6ffbdae62514d32f19d2751f4e674fc7cc887ffd224d298ddc1d51c8ef9eb
7
+ data.tar.gz: 14681e16b67254a763d33175c46b0aa28064153b7f338fc72d94dff9a338c4c722e202d6fc36c3bf86e0cb0d36beb4fd2b6ad2492ea88f5c3cc7956096876bc9
data/README.md CHANGED
@@ -156,7 +156,7 @@ end
156
156
 
157
157
  ## Built-in preview renderer
158
158
 
159
- The renderer is powered by [`Commonmarker`](https://github.com/gjtorikian/commonmarker) by default but it can be changed to [`Redcarpet`](https://github.com/vmg/redcarpet) in the configuration or add your own logic by customizing the `Marksmith::Renderer` model.
159
+ The renderer is powered by [`Commonmarker`](https://github.com/gjtorikian/commonmarker) by default but it can be changed to [`Redcarpet`](https://github.com/vmg/redcarpet) or ['kramdown'](https://github.com/gettalong/kramdown) in the configuration or add your own logic by customizing the `Marksmith::Renderer` model.
160
160
  It supports basic styles like headings, `strong`, `italic` and others.
161
161
 
162
162
  In your `show.html.erb` view or the place where you want to render the compiled markup use the `marksmithed` helper and it will run the content through the renderer.
@@ -168,7 +168,7 @@ In your `show.html.erb` view or the place where you want to render the compiled
168
168
  > [!WARNING]
169
169
  > Using the `<%==` tag will output the raw HTML, so ensure you sanitize the content to avoid XSS attacks.
170
170
  >
171
- > See how we do it [here](https://github.com/avo-hq/avo/blob/main/app/views/marksmith/shared/_rendered_body.html.erb#L2).
171
+ > See how we do it [here](app/views/marksmith/shared/_rendered_body.html.erb#L2).
172
172
  > ```ruby
173
173
  > # sample sanitization
174
174
  > sanitize(body, tags: %w(table th tr td span) + ActionView::Helpers::SanitizeHelper.sanitizer_vendor.safe_list_sanitizer.allowed_tags.to_a)
@@ -176,7 +176,7 @@ In your `show.html.erb` view or the place where you want to render the compiled
176
176
 
177
177
  ## Customize the renderer
178
178
 
179
- Marksmith comes with a default renderer that uses `Commonmarker` by default but it can be changed to `Redcarpet` in the configuration.
179
+ Marksmith comes with a default renderer that uses `commonmarker` by default but it can be changed to `redcarpet` or `kramdown` in the configuration.
180
180
 
181
181
  ```ruby
182
182
  # config/initializers/marksmith.rb
@@ -225,6 +225,16 @@ application.register('marksmith', MarksmithController)
225
225
  application.register('list-continuation', ListContinuationController)
226
226
  ```
227
227
 
228
+ ## Dark mode
229
+
230
+ Marksmith comes with dark mode built in using the `.dark` class on the `html` element strategy.
231
+
232
+ ```html
233
+ <html class="dark">
234
+ ...
235
+ </html>
236
+ ```
237
+
228
238
  ## Who uses Marksmith?
229
239
 
230
240
  - [Avo](https://avohq.io) - Ruby on Rails Code-Based App Builder Framework
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="ms:inline ms:size-4">
2
2
  <path stroke-linejoin="round" d="M6.75 3.744h-.753v8.25h7.125a4.125 4.125 0 0 0 0-8.25H6.75Zm0 0v.38m0 16.122h6.747a4.5 4.5 0 0 0 0-9.001h-7.5v9h.753Zm0 0v-.37m0-15.751h6a3.75 3.75 0 1 1 0 7.5h-6m0-7.5v7.5m0 0v8.25m0-8.25h6.375a4.125 4.125 0 0 1 0 8.25H6.75m.747-15.38h4.875a3.375 3.375 0 0 1 0 6.75H7.497v-6.75Zm0 7.5h5.25a3.75 3.75 0 0 1 0 7.5h-5.25v-7.5Z"/>
3
3
  </svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>
3
3
  </svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"/>
3
3
  </svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-images"><path d="M18 22H4a2 2 0 0 1-2-2V6"/><path d="m22 13-1.296-1.296a2.41 2.41 0 0 0-3.408 0L11 18"/><circle cx="12" cy="8" r="2"/><rect width="16" height="16" x="6" y="2" rx="2"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ms:inline ms:size-4"><path d="M18 22H4a2 2 0 0 1-2-2V6"/><path d="m22 13-1.296-1.296a2.41 2.41 0 0 0-3.408 0L11 18"/><circle cx="12" cy="8" r="2"/><rect width="16" height="16" x="6" y="2" rx="2"/></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-heading"><path d="M6 12h12"/><path d="M6 20V4"/><path d="M18 20V4"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ms:inline ms:size-4"><path d="M6 12h12"/><path d="M6 20V4"/><path d="M18 20V4"/></svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="m2.25 15.75 5.159-5.159a2.25 2.25 0 0 1 3.182 0l5.159 5.159m-1.5-1.5 1.409-1.409a2.25 2.25 0 0 1 3.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 0 0 1.5-1.5V6a1.5 1.5 0 0 0-1.5-1.5H3.75A1.5 1.5 0 0 0 2.25 6v12a1.5 1.5 0 0 0 1.5 1.5Zm10.5-11.25h.008v.008h-.008V8.25Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"/>
3
3
  </svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="M5.248 20.246H9.05m0 0h3.696m-3.696 0 5.893-16.502m0 0h-3.697m3.697 0h3.803"/>
3
3
  </svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"/>
3
3
  </svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" role="img"><script xmlns="" src="chrome-extension://hoklmmgfnpapgjgcpechhaamimifchmp/frame_ant/frame_ant.js"/><title>Markdown icon</title><path d="M22.269 19.385H1.731a1.73 1.73 0 0 1-1.73-1.73V6.345a1.73 1.73 0 0 1 1.73-1.73h20.538a1.73 1.73 0 0 1 1.73 1.73v11.308a1.73 1.73 0 0 1-1.73 1.731zm-16.5-3.462v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.461v7.847zM21.231 12h-2.308V8.077h-2.307V12h-2.308l3.461 4.039z"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" role="img" class="ms:inline ms:size-4"><script xmlns="" src="chrome-extension://hoklmmgfnpapgjgcpechhaamimifchmp/frame_ant/frame_ant.js"/><title>Markdown icon</title><path d="M22.269 19.385H1.731a1.73 1.73 0 0 1-1.73-1.73V6.345a1.73 1.73 0 0 1 1.73-1.73h20.538a1.73 1.73 0 0 1 1.73 1.73v11.308a1.73 1.73 0 0 1-1.73 1.731zm-16.5-3.462v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.461v7.847zM21.231 12h-2.308V8.077h-2.307V12h-2.308l3.461 4.039z"/></svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="M8.242 5.992h12m-12 6.003H20.24m-12 5.999h12M4.117 7.495v-3.75H2.99m1.125 3.75H2.99m1.125 0H5.24m-1.92 2.577a1.125 1.125 0 1 1 1.591 1.59l-1.83 1.83h2.16M2.99 15.745h1.125a1.125 1.125 0 0 1 0 2.25H3.74m0-.002h.375a1.125 1.125 0 0 1 0 2.25H2.99"/>
3
3
  </svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
3
3
  </svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-text-quote"><path d="M17 6H3"/><path d="M21 12H8"/><path d="M21 18H8"/><path d="M3 12v6"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ms:inline ms:size-4"><path d="M17 6H3"/><path d="M21 12H8"/><path d="M21 18H8"/><path d="M3 12v6"/></svg>
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-list-todo"><rect x="3" y="5" width="6" height="6" rx="1"/><path d="m3 17 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ms:inline ms:size-4"><rect x="3" y="5" width="6" height="6" rx="1"/><path d="m3 17 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/></svg>
@@ -1,3 +1,3 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon" class="ms:inline ms:size-4">
2
2
  <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM3.75 12h.007v.008H3.75V12Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.375 5.25h.007v.008H3.75v-.008Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Z"/>
3
3
  </svg>
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.3.0
2
+ Marksmith 0.4.0
3
3
  */
4
4
  var ListContinuationController = (function () {
5
5
  'use strict';
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.3.0
2
+ Marksmith 0.4.0
3
3
  */
4
4
  var ListContinuationController = (function (stimulus) {
5
5
  'use strict';
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.3.0
2
+ Marksmith 0.4.0
3
3
  */
4
4
  var MarksmithController = (function () {
5
5
  'use strict';
@@ -2830,7 +2830,9 @@ var MarksmithController = (function () {
2830
2830
  fileUploadsEnabled: { type: Boolean, default: true },
2831
2831
  }
2832
2832
 
2833
- static targets = ['fieldContainer', 'fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
2833
+ static targets = ['fieldContainer', 'fieldElement', 'previewPane', 'writeTabButton', 'previewTabButton', 'toolbar']
2834
+
2835
+ activeTabClass = "active"
2834
2836
 
2835
2837
  get #fileUploadsDisabled() {
2836
2838
  return !this.fileUploadsEnabledValue
@@ -2844,42 +2846,47 @@ var MarksmithController = (function () {
2844
2846
  event.preventDefault();
2845
2847
 
2846
2848
  // toggle buttons
2847
- this.writeTabButtonTarget.classList.add('ms:hidden');
2848
- this.previewTabButtonTarget.classList.remove('ms:hidden');
2849
+ this.writeTabButtonTarget.classList.add(this.activeTabClass);
2850
+ this.previewTabButtonTarget.classList.remove(this.activeTabClass);
2849
2851
 
2850
2852
  // toggle write/preview buttons
2851
2853
  this.fieldContainerTarget.classList.remove('ms:hidden');
2852
- this.previewElementTarget.classList.add('ms:hidden');
2854
+ this.previewPaneTarget.classList.add('ms:hidden');
2853
2855
 
2854
2856
  // toggle the toolbar back
2855
- this.toolbarTarget.classList.remove('ms:hidden');
2857
+ this.toolbarTarget.classList.remove('ms:opacity-0', 'ms:pointer-events-none');
2856
2858
  }
2857
2859
 
2858
2860
  switchToPreview(event) {
2859
2861
  event.preventDefault();
2860
2862
 
2863
+ // unfocus the active element to hide the outline around the editor
2864
+ this.element.focus();
2865
+ this.element.blur();
2866
+ document.activeElement.blur();
2867
+
2861
2868
  post(this.previewUrlValue, {
2862
2869
  body: {
2863
2870
  body: this.fieldElementTarget.value,
2864
- element_id: this.previewElementTarget.id,
2871
+ element_id: this.previewPaneTarget.id,
2865
2872
  extra_params: this.extraPreviewParamsValue,
2866
2873
  },
2867
2874
  responseKind: 'turbo-stream',
2868
2875
  });
2869
2876
 
2870
2877
  // set the min height to the field element height
2871
- this.previewElementTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`;
2878
+ this.previewPaneTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`;
2872
2879
 
2873
2880
  // toggle buttons
2874
- this.writeTabButtonTarget.classList.remove('ms:hidden');
2875
- this.previewTabButtonTarget.classList.add('ms:hidden');
2881
+ this.writeTabButtonTarget.classList.remove(this.activeTabClass);
2882
+ this.previewTabButtonTarget.classList.add(this.activeTabClass);
2876
2883
 
2877
2884
  // toggle elements
2878
2885
  this.fieldContainerTarget.classList.add('ms:hidden');
2879
- this.previewElementTarget.classList.remove('ms:hidden');
2886
+ this.previewPaneTarget.classList.remove('ms:hidden');
2880
2887
 
2881
2888
  // toggle the toolbar
2882
- this.toolbarTarget.classList.add('ms:hidden');
2889
+ this.toolbarTarget.classList.add('ms:opacity-0', 'ms:pointer-events-none');
2883
2890
  }
2884
2891
 
2885
2892
  dropUpload(event) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- Marksmith 0.3.0
2
+ Marksmith 0.4.0
3
3
  */
4
4
  var MarksmithController = (function (stimulus) {
5
5
  'use strict';
@@ -2340,7 +2340,9 @@ var MarksmithController = (function (stimulus) {
2340
2340
  fileUploadsEnabled: { type: Boolean, default: true },
2341
2341
  }
2342
2342
 
2343
- static targets = ['fieldContainer', 'fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
2343
+ static targets = ['fieldContainer', 'fieldElement', 'previewPane', 'writeTabButton', 'previewTabButton', 'toolbar']
2344
+
2345
+ activeTabClass = "active"
2344
2346
 
2345
2347
  get #fileUploadsDisabled() {
2346
2348
  return !this.fileUploadsEnabledValue
@@ -2354,42 +2356,47 @@ var MarksmithController = (function (stimulus) {
2354
2356
  event.preventDefault();
2355
2357
 
2356
2358
  // toggle buttons
2357
- this.writeTabButtonTarget.classList.add('ms:hidden');
2358
- this.previewTabButtonTarget.classList.remove('ms:hidden');
2359
+ this.writeTabButtonTarget.classList.add(this.activeTabClass);
2360
+ this.previewTabButtonTarget.classList.remove(this.activeTabClass);
2359
2361
 
2360
2362
  // toggle write/preview buttons
2361
2363
  this.fieldContainerTarget.classList.remove('ms:hidden');
2362
- this.previewElementTarget.classList.add('ms:hidden');
2364
+ this.previewPaneTarget.classList.add('ms:hidden');
2363
2365
 
2364
2366
  // toggle the toolbar back
2365
- this.toolbarTarget.classList.remove('ms:hidden');
2367
+ this.toolbarTarget.classList.remove('ms:opacity-0', 'ms:pointer-events-none');
2366
2368
  }
2367
2369
 
2368
2370
  switchToPreview(event) {
2369
2371
  event.preventDefault();
2370
2372
 
2373
+ // unfocus the active element to hide the outline around the editor
2374
+ this.element.focus();
2375
+ this.element.blur();
2376
+ document.activeElement.blur();
2377
+
2371
2378
  post(this.previewUrlValue, {
2372
2379
  body: {
2373
2380
  body: this.fieldElementTarget.value,
2374
- element_id: this.previewElementTarget.id,
2381
+ element_id: this.previewPaneTarget.id,
2375
2382
  extra_params: this.extraPreviewParamsValue,
2376
2383
  },
2377
2384
  responseKind: 'turbo-stream',
2378
2385
  });
2379
2386
 
2380
2387
  // set the min height to the field element height
2381
- this.previewElementTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`;
2388
+ this.previewPaneTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`;
2382
2389
 
2383
2390
  // toggle buttons
2384
- this.writeTabButtonTarget.classList.remove('ms:hidden');
2385
- this.previewTabButtonTarget.classList.add('ms:hidden');
2391
+ this.writeTabButtonTarget.classList.remove(this.activeTabClass);
2392
+ this.previewTabButtonTarget.classList.add(this.activeTabClass);
2386
2393
 
2387
2394
  // toggle elements
2388
2395
  this.fieldContainerTarget.classList.add('ms:hidden');
2389
- this.previewElementTarget.classList.remove('ms:hidden');
2396
+ this.previewPaneTarget.classList.remove('ms:hidden');
2390
2397
 
2391
2398
  // toggle the toolbar
2392
- this.toolbarTarget.classList.add('ms:hidden');
2399
+ this.toolbarTarget.classList.add('ms:opacity-0', 'ms:pointer-events-none');
2393
2400
  }
2394
2401
 
2395
2402
  dropUpload(event) {
@@ -373,6 +373,9 @@
373
373
  .ms\:pointer-events-none {
374
374
  pointer-events: none;
375
375
  }
376
+ .ms\:-my-px {
377
+ margin-block: -1px;
378
+ }
376
379
  .ms\:prose {
377
380
  color: var(--tw-prose-body);
378
381
  max-width: 65ch;
@@ -788,8 +791,8 @@
788
791
  margin-bottom: 0;
789
792
  }
790
793
  }
791
- .ms\:mr-1 {
792
- margin-right: calc(var(--ms-spacing) * 1);
794
+ .ms\:-ml-px {
795
+ margin-left: -1px;
793
796
  }
794
797
  .ms\:block {
795
798
  display: block;
@@ -814,6 +817,9 @@
814
817
  width: 100%;
815
818
  height: 100%;
816
819
  }
820
+ .ms\:h-\[calc\(100\%\+3px\)\] {
821
+ height: calc(100% + 3px);
822
+ }
817
823
  .ms\:min-h-60 {
818
824
  min-height: calc(var(--ms-spacing) * 60);
819
825
  }
@@ -850,6 +856,9 @@
850
856
  .ms\:items-center {
851
857
  align-items: center;
852
858
  }
859
+ .ms\:gap-1 {
860
+ gap: calc(var(--ms-spacing) * 1);
861
+ }
853
862
  .ms\:gap-y-1 {
854
863
  row-gap: calc(var(--ms-spacing) * 1);
855
864
  }
@@ -866,14 +875,21 @@
866
875
  .ms\:rounded {
867
876
  border-radius: 0.25rem;
868
877
  }
878
+ .ms\:rounded-md {
879
+ border-radius: var(--ms-radius-md);
880
+ }
881
+ .ms\:rounded-t-md {
882
+ border-top-left-radius: var(--ms-radius-md);
883
+ border-top-right-radius: var(--ms-radius-md);
884
+ }
885
+ .ms\:rounded-b-md {
886
+ border-bottom-right-radius: var(--ms-radius-md);
887
+ border-bottom-left-radius: var(--ms-radius-md);
888
+ }
869
889
  .ms\:border {
870
890
  border-style: var(--tw-border-style);
871
891
  border-width: 1px;
872
892
  }
873
- .ms\:border-0 {
874
- border-style: var(--tw-border-style);
875
- border-width: 0px;
876
- }
877
893
  .ms\:border-t {
878
894
  border-top-style: var(--tw-border-style);
879
895
  border-top-width: 1px;
@@ -882,18 +898,21 @@
882
898
  --tw-border-style: none;
883
899
  border-style: none;
884
900
  }
885
- .ms\:border-neutral-300 {
886
- border-color: var(--ms-color-neutral-300);
901
+ .ms\:border-neutral-500 {
902
+ border-color: var(--ms-color-neutral-500);
903
+ }
904
+ .ms\:border-transparent {
905
+ border-color: transparent;
887
906
  }
888
907
  .ms\:bg-neutral-50 {
889
908
  background-color: var(--ms-color-neutral-50);
890
909
  }
891
- .ms\:bg-neutral-200 {
892
- background-color: var(--ms-color-neutral-200);
893
- }
894
910
  .ms\:bg-transparent {
895
911
  background-color: transparent;
896
912
  }
913
+ .ms\:bg-white {
914
+ background-color: var(--ms-color-white);
915
+ }
897
916
  .ms\:bg-none {
898
917
  background-image: none;
899
918
  }
@@ -909,6 +928,9 @@
909
928
  .ms\:px-2 {
910
929
  padding-inline: calc(var(--ms-spacing) * 2);
911
930
  }
931
+ .ms\:px-3 {
932
+ padding-inline: calc(var(--ms-spacing) * 3);
933
+ }
912
934
  .ms\:py-1 {
913
935
  padding-block: calc(var(--ms-spacing) * 1);
914
936
  }
@@ -925,17 +947,16 @@
925
947
  font-size: var(--ms-text-sm);
926
948
  line-height: var(--tw-leading, var(--ms-text-sm--line-height));
927
949
  }
928
- .ms\:text-xs {
929
- font-size: var(--ms-text-xs);
930
- line-height: var(--tw-leading, var(--ms-text-xs--line-height));
931
- }
932
950
  .ms\:leading-normal {
933
951
  --tw-leading: var(--ms-leading-normal);
934
952
  line-height: var(--ms-leading-normal);
935
953
  }
936
- .ms\:font-semibold {
937
- --tw-font-weight: var(--ms-font-weight-semibold);
938
- font-weight: var(--ms-font-weight-semibold);
954
+ .ms\:font-medium {
955
+ --tw-font-weight: var(--ms-font-weight-medium);
956
+ font-weight: var(--ms-font-weight-medium);
957
+ }
958
+ .ms\:text-neutral-500 {
959
+ color: var(--ms-color-neutral-500);
939
960
  }
940
961
  .ms\:text-neutral-600 {
941
962
  color: var(--ms-color-neutral-600);
@@ -943,12 +964,18 @@
943
964
  .ms\:text-neutral-800 {
944
965
  color: var(--ms-color-neutral-800);
945
966
  }
946
- .ms\:uppercase {
947
- text-transform: uppercase;
948
- }
949
967
  .ms\:no-underline {
950
968
  text-decoration-line: none;
951
969
  }
970
+ .ms\:opacity-0 {
971
+ opacity: 0%;
972
+ }
973
+ .ms\:-outline-offset-1 {
974
+ outline-offset: calc(1px * -1);
975
+ }
976
+ .ms\:outline-blue-500 {
977
+ outline-color: var(--ms-color-blue-500);
978
+ }
952
979
  .ms\:prose-neutral {
953
980
  --tw-prose-body: oklch(0.371 0 0);
954
981
  --tw-prose-headings: oklch(0.205 0 0);
@@ -987,29 +1014,30 @@
987
1014
  --tw-prose-invert-th-borders: oklch(0.439 0 0);
988
1015
  --tw-prose-invert-td-borders: oklch(0.371 0 0);
989
1016
  }
990
- .ms\:focus-within\:border-neutral-400 {
1017
+ .ms\:focus-within\:outline-2 {
991
1018
  &:focus-within {
992
- border-color: var(--ms-color-neutral-400);
1019
+ outline-style: var(--tw-outline-style);
1020
+ outline-width: 2px;
993
1021
  }
994
1022
  }
995
- .ms\:hover\:bg-neutral-100 {
1023
+ .ms\:hover\:bg-neutral-200 {
996
1024
  &:hover {
997
1025
  @media (hover: hover) {
998
- background-color: var(--ms-color-neutral-100);
1026
+ background-color: var(--ms-color-neutral-200);
999
1027
  }
1000
1028
  }
1001
1029
  }
1002
- .ms\:hover\:bg-neutral-200 {
1030
+ .ms\:hover\:bg-neutral-300 {
1003
1031
  &:hover {
1004
1032
  @media (hover: hover) {
1005
- background-color: var(--ms-color-neutral-200);
1033
+ background-color: var(--ms-color-neutral-300);
1006
1034
  }
1007
1035
  }
1008
1036
  }
1009
- .ms\:hover\:bg-neutral-300 {
1037
+ .ms\:hover\:bg-transparent {
1010
1038
  &:hover {
1011
1039
  @media (hover: hover) {
1012
- background-color: var(--ms-color-neutral-300);
1040
+ background-color: transparent;
1013
1041
  }
1014
1042
  }
1015
1043
  }
@@ -1030,6 +1058,109 @@
1030
1058
  flex-direction: row;
1031
1059
  }
1032
1060
  }
1061
+ .ms\:dark\:bg-neutral-700 {
1062
+ &:where(.dark, .dark *) {
1063
+ background-color: var(--ms-color-neutral-700);
1064
+ }
1065
+ }
1066
+ .ms\:dark\:bg-neutral-800 {
1067
+ &:where(.dark, .dark *) {
1068
+ background-color: var(--ms-color-neutral-800);
1069
+ }
1070
+ }
1071
+ .ms\:dark\:bg-neutral-900 {
1072
+ &:where(.dark, .dark *) {
1073
+ background-color: var(--ms-color-neutral-900);
1074
+ }
1075
+ }
1076
+ .ms\:dark\:text-neutral-50 {
1077
+ &:where(.dark, .dark *) {
1078
+ color: var(--ms-color-neutral-50);
1079
+ }
1080
+ }
1081
+ .ms\:dark\:text-neutral-200 {
1082
+ &:where(.dark, .dark *) {
1083
+ color: var(--ms-color-neutral-200);
1084
+ }
1085
+ }
1086
+ .ms\:dark\:text-neutral-300 {
1087
+ &:where(.dark, .dark *) {
1088
+ color: var(--ms-color-neutral-300);
1089
+ }
1090
+ }
1091
+ .ms\:dark\:prose-invert {
1092
+ &:where(.dark, .dark *) {
1093
+ --tw-prose-body: var(--tw-prose-invert-body);
1094
+ --tw-prose-headings: var(--tw-prose-invert-headings);
1095
+ --tw-prose-lead: var(--tw-prose-invert-lead);
1096
+ --tw-prose-links: var(--tw-prose-invert-links);
1097
+ --tw-prose-bold: var(--tw-prose-invert-bold);
1098
+ --tw-prose-counters: var(--tw-prose-invert-counters);
1099
+ --tw-prose-bullets: var(--tw-prose-invert-bullets);
1100
+ --tw-prose-hr: var(--tw-prose-invert-hr);
1101
+ --tw-prose-quotes: var(--tw-prose-invert-quotes);
1102
+ --tw-prose-quote-borders: var(--tw-prose-invert-quote-borders);
1103
+ --tw-prose-captions: var(--tw-prose-invert-captions);
1104
+ --tw-prose-kbd: var(--tw-prose-invert-kbd);
1105
+ --tw-prose-kbd-shadows: var(--tw-prose-invert-kbd-shadows);
1106
+ --tw-prose-code: var(--tw-prose-invert-code);
1107
+ --tw-prose-pre-code: var(--tw-prose-invert-pre-code);
1108
+ --tw-prose-pre-bg: var(--tw-prose-invert-pre-bg);
1109
+ --tw-prose-th-borders: var(--tw-prose-invert-th-borders);
1110
+ --tw-prose-td-borders: var(--tw-prose-invert-td-borders);
1111
+ }
1112
+ }
1113
+ .ms\:dark\:hover\:bg-neutral-600 {
1114
+ &:where(.dark, .dark *) {
1115
+ &:hover {
1116
+ @media (hover: hover) {
1117
+ background-color: var(--ms-color-neutral-600);
1118
+ }
1119
+ }
1120
+ }
1121
+ }
1122
+ .ms\:\[\.active\]\:rounded-t-md {
1123
+ &:is(.active) {
1124
+ border-top-left-radius: var(--ms-radius-md);
1125
+ border-top-right-radius: var(--ms-radius-md);
1126
+ }
1127
+ }
1128
+ .ms\:\[\.active\]\:border-neutral-500 {
1129
+ &:is(.active) {
1130
+ border-color: var(--ms-color-neutral-500);
1131
+ }
1132
+ }
1133
+ .ms\:\[\.active\]\:bg-neutral-50 {
1134
+ &:is(.active) {
1135
+ background-color: var(--ms-color-neutral-50);
1136
+ }
1137
+ }
1138
+ .ms\:\[\.active\]\:text-neutral-900 {
1139
+ &:is(.active) {
1140
+ color: var(--ms-color-neutral-900);
1141
+ }
1142
+ }
1143
+ .ms\:\[\.active\]\:dark\:border-neutral-500 {
1144
+ &:is(.active) {
1145
+ &:where(.dark, .dark *) {
1146
+ border-color: var(--ms-color-neutral-500);
1147
+ }
1148
+ }
1149
+ }
1150
+ .ms\:\[\.active\]\:dark\:bg-neutral-800 {
1151
+ &:is(.active) {
1152
+ &:where(.dark, .dark *) {
1153
+ background-color: var(--ms-color-neutral-800);
1154
+ }
1155
+ }
1156
+ }
1157
+ .ms\:dark\:\[\.active\]\:text-neutral-300 {
1158
+ &:where(.dark, .dark *) {
1159
+ &:is(.active) {
1160
+ color: var(--ms-color-neutral-300);
1161
+ }
1162
+ }
1163
+ }
1033
1164
  .marksmith, .marksmith * {
1034
1165
  box-sizing: border-box;
1035
1166
  }
@@ -1116,6 +1247,11 @@
1116
1247
  syntax: "*";
1117
1248
  inherits: false;
1118
1249
  }
1250
+ @property --tw-outline-style {
1251
+ syntax: "*";
1252
+ inherits: false;
1253
+ initial-value: solid;
1254
+ }
1119
1255
  @property --tw-shadow {
1120
1256
  syntax: "*";
1121
1257
  inherits: false;
@@ -8,10 +8,15 @@
8
8
 
9
9
  @plugin "@tailwindcss/typography";
10
10
 
11
+ @source "./../../assets/images/marksmith/svgs/";
11
12
  @source "./../../views/";
13
+ @source "./javascript/controllers/";
14
+ @source "./../../../test/dummy/app/views/";
12
15
  @source "./../../helpers/";
13
16
  @source "./../../../lib/";
14
17
 
18
+ @custom-variant dark (&:where(.dark, .dark *));
19
+
15
20
  .marksmith,
16
21
  .marksmith * {
17
22
  box-sizing: border-box;
@@ -20,7 +20,9 @@ export default class extends Controller {
20
20
  fileUploadsEnabled: { type: Boolean, default: true },
21
21
  }
22
22
 
23
- static targets = ['fieldContainer', 'fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
23
+ static targets = ['fieldContainer', 'fieldElement', 'previewPane', 'writeTabButton', 'previewTabButton', 'toolbar']
24
+
25
+ activeTabClass = "active"
24
26
 
25
27
  get #fileUploadsDisabled() {
26
28
  return !this.fileUploadsEnabledValue
@@ -34,42 +36,47 @@ export default class extends Controller {
34
36
  event.preventDefault()
35
37
 
36
38
  // toggle buttons
37
- this.writeTabButtonTarget.classList.add('ms:hidden')
38
- this.previewTabButtonTarget.classList.remove('ms:hidden')
39
+ this.writeTabButtonTarget.classList.add(this.activeTabClass)
40
+ this.previewTabButtonTarget.classList.remove(this.activeTabClass)
39
41
 
40
42
  // toggle write/preview buttons
41
43
  this.fieldContainerTarget.classList.remove('ms:hidden')
42
- this.previewElementTarget.classList.add('ms:hidden')
44
+ this.previewPaneTarget.classList.add('ms:hidden')
43
45
 
44
46
  // toggle the toolbar back
45
- this.toolbarTarget.classList.remove('ms:hidden')
47
+ this.toolbarTarget.classList.remove('ms:opacity-0', 'ms:pointer-events-none')
46
48
  }
47
49
 
48
50
  switchToPreview(event) {
49
51
  event.preventDefault()
50
52
 
53
+ // unfocus the active element to hide the outline around the editor
54
+ this.element.focus()
55
+ this.element.blur()
56
+ document.activeElement.blur()
57
+
51
58
  post(this.previewUrlValue, {
52
59
  body: {
53
60
  body: this.fieldElementTarget.value,
54
- element_id: this.previewElementTarget.id,
61
+ element_id: this.previewPaneTarget.id,
55
62
  extra_params: this.extraPreviewParamsValue,
56
63
  },
57
64
  responseKind: 'turbo-stream',
58
65
  })
59
66
 
60
67
  // set the min height to the field element height
61
- this.previewElementTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`
68
+ this.previewPaneTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`
62
69
 
63
70
  // toggle buttons
64
- this.writeTabButtonTarget.classList.remove('ms:hidden')
65
- this.previewTabButtonTarget.classList.add('ms:hidden')
71
+ this.writeTabButtonTarget.classList.remove(this.activeTabClass)
72
+ this.previewTabButtonTarget.classList.add(this.activeTabClass)
66
73
 
67
74
  // toggle elements
68
75
  this.fieldContainerTarget.classList.add('ms:hidden')
69
- this.previewElementTarget.classList.remove('ms:hidden')
76
+ this.previewPaneTarget.classList.remove('ms:hidden')
70
77
 
71
78
  // toggle the toolbar
72
- this.toolbarTarget.classList.add('ms:hidden')
79
+ this.toolbarTarget.classList.add('ms:opacity-0', 'ms:pointer-events-none')
73
80
  }
74
81
 
75
82
  dropUpload(event) {
@@ -0,0 +1,59 @@
1
+ module Marksmith
2
+ module MarksmithHelper
3
+ def marksmithed(body)
4
+ Marksmith::Renderer.new(body:).render
5
+ end
6
+
7
+ def marksmith_tag(name, **kwargs, &block)
8
+ rails_direct_uploads_url = if defined?(ActiveStorage)
9
+ main_app.rails_direct_uploads_url
10
+ end
11
+
12
+ editor = Marksmith::Editor.new(name:, rails_direct_uploads_url:, **kwargs, &block)
13
+
14
+ render partial: "marksmith/shared/editor", locals: { name: editor.name, editor: }
15
+ end
16
+
17
+ def marksmith_asset_tags(*args, **kwargs)
18
+ stylesheet_link_tag("marksmith", *args, **kwargs) +
19
+ javascript_include_tag("marksmith.esm.js", *args, **kwargs)
20
+ end
21
+
22
+ def marksmith_button_classes
23
+ class_names(
24
+ "ms:flex ms:items:center ms:cursor-pointer ms:py-1 ms:px-1.5 ms:hover:bg-neutral-200 ms:rounded",
25
+ "ms:dark:text-neutral-300 ms:dark:hover:bg-neutral-600"
26
+ )
27
+ end
28
+
29
+ def marksmith_toolbar_button(name, **kwargs)
30
+ content_tag "md-#{name}", marksmith_toolbar_svg(name), title: t("marksmith.#{name.to_s.gsub("-", "_")}").humanize, class: marksmith_button_classes
31
+ end
32
+
33
+ def marksmith_tab_classes
34
+ class_names(
35
+ # marksmith_button_classes,
36
+ "marksmith-toggle-button ms:text-sm ms:hover:bg-neutral-300 ms:text-sm ms:font-medium ms:cursor-pointer ms:text-neutral-500 ms:px-3",
37
+ # borders
38
+ "ms:bg-transparent ms:hover:bg-transparent",
39
+ "ms:-my-px ms:-ml-px ms:border ms:border-transparent",
40
+ "ms:h-[calc(100%+3px)] ms:border-b-none",
41
+ # "ms:border-b-neutral-00",
42
+ # active classes
43
+ "ms:[.active]:bg-neutral-50 ms:[.active]:text-neutral-900 ms:dark:[.active]:text-neutral-300 ms:[.active]:dark:bg-neutral-800 ms:[.active]:dark:border-neutral-500 ms:[.active]:rounded-t-md ms:[.active]:border-neutral-500",
44
+
45
+ )
46
+ end
47
+
48
+ def marksmith_inline_svg(path)
49
+ File.open(Marksmith::Engine.root.join(path)).read.html_safe
50
+ end
51
+
52
+ # TODO: maybe inline svgs in the future
53
+ def marksmith_toolbar_svg(name)
54
+ marksmith_inline_svg("app/assets/images/marksmith/svgs/#{name}.svg")
55
+ rescue
56
+ "<!-- Failed to load SVG for #{name} -->"
57
+ end
58
+ end
59
+ end
@@ -2,17 +2,18 @@ require "uri"
2
2
 
3
3
  class Marksmith::Editor
4
4
  attr_reader :name,
5
- :extra_preview_params,
6
- :form,
7
- :disabled,
8
- :controller_data_attributes,
9
- :classes,
10
- :data_attributes,
11
- :placeholder,
12
- :autofocus,
13
- :style,
14
- :gallery,
15
- :kwargs
5
+ :extra_preview_params,
6
+ :form,
7
+ :disabled,
8
+ :controller_data_attributes,
9
+ :classes,
10
+ :data_attributes,
11
+ :placeholder,
12
+ :autofocus,
13
+ :style,
14
+ :gallery,
15
+ :kwargs,
16
+ :id
16
17
 
17
18
  def initialize(name:,
18
19
  upload_url: nil,
@@ -27,6 +28,8 @@ class Marksmith::Editor
27
28
  placeholder: nil,
28
29
  autofocus: false,
29
30
  style: nil,
31
+ value: nil,
32
+ id: "marksmith-instance-#{rand(1000..9999)}",
30
33
  gallery: {},
31
34
  **kwargs)
32
35
  @name = name
@@ -44,6 +47,8 @@ class Marksmith::Editor
44
47
  @placeholder = placeholder
45
48
  @autofocus = autofocus
46
49
  @style = style
50
+ @value = value
51
+ @id = id
47
52
  @gallery = gallery
48
53
  end
49
54
 
@@ -94,10 +99,6 @@ class Marksmith::Editor
94
99
  end
95
100
 
96
101
  def value
97
- if defined?(form)
98
- form&.object&.send(name)
99
- else
100
- @value || nil
101
- end
102
+ form&.object&.send(name) || @value || nil
102
103
  end
103
104
  end
@@ -7,6 +7,8 @@ module Marksmith
7
7
  def render
8
8
  if Marksmith.configuration.parser == "commonmarker"
9
9
  render_commonmarker
10
+ elsif Marksmith.configuration.parser == "kramdown"
11
+ render_kramdown
10
12
  else
11
13
  render_redcarpet
12
14
  end
@@ -14,7 +16,7 @@ module Marksmith
14
16
 
15
17
  def render_commonmarker
16
18
  # commonmarker expects an utf-8 encoded string
17
- body = @body.to_s.dup.force_encoding('utf-8')
19
+ body = @body.to_s.dup.force_encoding("utf-8")
18
20
  Commonmarker.to_html(body)
19
21
  end
20
22
 
@@ -34,5 +36,10 @@ module Marksmith
34
36
  with_toc_data: true
35
37
  ).render(@body)
36
38
  end
39
+
40
+ def render_kramdown
41
+ body = @body.to_s.dup.force_encoding("utf-8")
42
+ Kramdown::Document.new(body).to_html
43
+ end
37
44
  end
38
45
  end
@@ -1,4 +1,7 @@
1
- <markdown-toolbar for="<%= name %>" class="<%= class_names("ms:flex ms:flex-wrap", "ms:pointer-events-none": disabled) %>" data-marksmith-target="toolbar">
1
+ <%= tag.markdown_toolbar for: name,
2
+ class: class_names("ms:flex ms:flex-wrap ms:px-2 ms:py-1", "ms:pointer-events-none": disabled),
3
+ data: { marksmith_target: "toolbar" } do
4
+ %>
2
5
  <%= marksmith_toolbar_button "bold" %>
3
6
  <%= marksmith_toolbar_button "header" %>
4
7
  <%= marksmith_toolbar_button "italic" %>
@@ -9,4 +12,4 @@
9
12
  <%= marksmith_toolbar_button "unordered-list" %>
10
13
  <%= marksmith_toolbar_button "ordered-list" %>
11
14
  <%= marksmith_toolbar_button "task-list" %>
12
- </markdown-toolbar>
15
+ <% end %>
@@ -1,5 +1,6 @@
1
1
  <%= content_tag :div,
2
- class: "marksmith ms:block ms:flex-col ms:w-full ms:border ms:border-neutral-300 ms:rounded ms:@container ms:focus-within:border-neutral-400",
2
+ id: editor.id,
3
+ class: "marksmith ms:block ms:flex-col ms:w-full ms:border ms:border-neutral-500 ms:rounded-md ms:@container ms:focus-within:outline-2 ms:outline-blue-500 ms:-outline-offset-1",
3
4
  data: {
4
5
  controller: "marksmith list-continuation",
5
6
  action: "
@@ -15,7 +16,7 @@
15
16
  **editor.controller_data_attributes,
16
17
  } do %>
17
18
  <%= render partial: "marksmith/shared/toolbar", locals: { name: editor.name, disabled: editor.disabled } %>
18
- <div class="ms:border-t ms:w-full ms:border-neutral-300 ms:flex ms:flex-1">
19
+ <div class="ms:border-t ms:w-full ms:border-neutral-500 ms:flex ms:flex-1">
19
20
  <%= render partial: "marksmith/shared/editor_pane", locals: { editor: } %>
20
21
  <%= render partial: "marksmith/shared/preview_pane", locals: { name: editor.name } %>
21
22
  </div>
@@ -1,8 +1,11 @@
1
- <% toolbar_button_classes = "ms:cursor-pointer ms:hover:bg-neutral-100 ms:px-1 ms:py-px ms:rounded ms:text-sm" %>
2
1
  <%= content_tag :div, class: "ms:flex ms:flex-1 ms:flex-col ms:size-full", data: { marksmith_target: "fieldContainer" } do %>
3
2
  <%= text_area_tag editor.field_name, editor.value,
4
3
  id: editor.name,
5
- class: class_names("ms:flex ms:flex-1 ms:rounded ms:border-none ms:resize-none ms:focus:outline-none ms:font-mono ms:focus:ring-0 ms:leading-normal ms:p-2 ms:text-sm ms:field-sizing-content ms:min-h-60", editor.classes),
4
+ class: class_names(
5
+ "ms:flex ms:flex-1 ms:border-none ms:resize-none ms:focus:outline-none ms:font-mono ms:focus:ring-0 ms:leading-normal ms:p-2 ms:text-sm ms:field-sizing-content ms:min-h-60",
6
+ "ms:dark:bg-neutral-800 ms:dark:text-neutral-200",
7
+ editor.classes
8
+ ),
6
9
  data: {
7
10
  action: "drop->marksmith#dropUpload paste->marksmith#pasteUpload",
8
11
  marksmith_target: "fieldElement",
@@ -13,18 +16,19 @@
13
16
  autofocus: editor.autofocus,
14
17
  style: editor.style
15
18
  %>
16
- <div class="ms:flex ms:flex-1 ms:flex-grow ms:space-x-2 ms:py-1 ms:border-t ms:border-neutral-300 ms:px-2 ms:font-sans ms:text-sm ms:p-2">
17
- <%= link_to "https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax", target: "_blank", class: class_names("ms:flex ms:items-center ms:text-neutral-800 ms:no-underline", toolbar_button_classes) do %>
18
- <%= image_tag asset_path("marksmith/svgs/markdown.svg"), class: "ms:inline ms:size-4 ms:mr-1" %> <%= t("marksmith.markdown_is_supported").humanize %>
19
+ <% toolbar_button_classes = "ms:cursor-pointer ms:hover:bg-neutral-200 ms:px-1 ms:py-px ms:rounded ms:text-sm ms:dark:text-neutral-300 ms:dark:hover:bg-neutral-600" %>
20
+ <div class="ms:flex ms:flex-1 ms:flex-grow ms:space-x-2 ms:py-1 ms:border-t ms:border-neutral-500 ms:px-2 ms:font-sans ms:text-sm ms:p-2 ms:dark:bg-neutral-800 ms:dark:text-neutral-300 ms:rounded-b-md">
21
+ <%= link_to "https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax", target: "_blank", class: class_names("ms:flex ms:items-center ms:text-neutral-800 ms:no-underline ms:gap-1", toolbar_button_classes) do %>
22
+ <%= marksmith_inline_svg("app/assets/images/marksmith/svgs/markdown.svg") %> <span><%= t("marksmith.markdown_is_supported").humanize %></span>
19
23
  <% end %>
20
24
  <% if editor.enable_file_uploads %>
21
- <%= button_tag data: { action: "click->marksmith#buttonUpload" }, class: class_names("ms:bg-none ms:border-none ms:bg-transparent ms:text-neutral-600 ms:items-center ms:flex", toolbar_button_classes) do %>
22
- <%= image_tag asset_path("marksmith/svgs/paperclip.svg"), class: "ms:inline ms:size-4 ms:mr-1" %> <%= t("marksmith.upload_files").humanize %>
25
+ <%= button_tag data: { action: "click->marksmith#buttonUpload" }, class: class_names("ms:bg-none ms:border-none ms:bg-transparent ms:text-neutral-600 ms:items-center ms:flex ms:gap-1", toolbar_button_classes) do %>
26
+ <%= marksmith_inline_svg("app/assets/images/marksmith/svgs/paperclip.svg") %> <span><%= t("marksmith.upload_files").humanize %></span>
23
27
  <% end %>
24
28
  <% end %>
25
29
  <% if editor.gallery_enabled %>
26
- <%= link_to editor.gallery_full_path, data: { turbo_frame: editor.gallery_turbo_frame }, class: class_names("ms:flex ms:items-center ms:text-neutral-800 ms:no-underline", toolbar_button_classes) do %>
27
- <%= image_tag asset_path("marksmith/svgs/gallery.svg"), class: "ms:inline ms:size-4 ms:mr-1" %> <%= t("marksmith.attach_from_gallery").humanize %>
30
+ <%= link_to editor.gallery_full_path, data: { turbo_frame: editor.gallery_turbo_frame }, class: class_names("ms:flex ms:items-center ms:text-neutral-800 ms:no-underline ms:gap-1", toolbar_button_classes) do %>
31
+ <%= marksmith_inline_svg("app/assets/images/marksmith/svgs/gallery.svg") %> <span><%= t("marksmith.attach_from_gallery").humanize %></span>
28
32
  <% end %>
29
33
  <% end %>
30
34
  </div>
@@ -1,8 +1,8 @@
1
1
  <%= content_tag :div,
2
- class: "ms:hidden ms:markdown-preview ms:size-full ms:flex-1 ms:flex ms:size-full ms:p-2 ms:overflow-auto",
2
+ class: "ms:hidden ms:markdown-preview ms:size-full ms:flex-1 ms:flex ms:size-full ms:p-2 ms:overflow-auto ms:bg-white ms:dark:bg-neutral-800 ms:rounded-b-md",
3
3
  id: "markdown-preview-#{name}",
4
4
  data: {
5
- marksmith_target: "previewElement",
5
+ marksmith_target: "previewPane",
6
6
  } do %>
7
7
  <%= render partial: "marksmith/shared/loading_indicator" %>
8
8
  <% end %>
@@ -1,3 +1,3 @@
1
- <%= content_tag :div, class: "ms:block ms:w-full ms:prose ms:max-w-none ms:prose-neutral" do %>
1
+ <%= content_tag :div, class: "ms:block ms:w-full ms:prose ms:max-w-none ms:prose-neutral ms:dark:prose-invert" do %>
2
2
  <%= sanitize(body, tags: %w(table th tr td span) + ActionView::Helpers::SanitizeHelper.sanitizer_vendor.safe_list_sanitizer.allowed_tags.to_a) %>
3
3
  <% end %>
@@ -1,8 +1,8 @@
1
1
  <div class="ms:flex-1 ms:flex ms:items:center">
2
- <button class="<%= marksmith_toggle_button_classes %>" data-action="click->marksmith#switchToPreview" data-marksmith-target="previewTabButton" type="button">
3
- <%= t('marksmith.preview').humanize %>
4
- </button>
5
- <button class="<%= marksmith_toggle_button_classes %> ms:hidden ms:bg-neutral-200" data-action="click->marksmith#switchToWrite" data-marksmith-target="writeTabButton" type="button">
2
+ <button class="<%= marksmith_tab_classes %> active" data-action="click->marksmith#switchToWrite" data-marksmith-target="writeTabButton" type="button">
6
3
  <%= t('marksmith.write').humanize %>
7
4
  </button>
5
+ <button class="<%= marksmith_tab_classes %>" data-action="click->marksmith#switchToPreview" data-marksmith-target="previewTabButton" type="button">
6
+ <%= t('marksmith.preview').humanize %>
7
+ </button>
8
8
  </div>
@@ -1,5 +1,8 @@
1
- <div class="ms:flex-1 ms:flex-col-reverse ms:@md:flex-row ms:grow ms:flex ms:justify-bewteen ms:bg-neutral-50 ms:rounded ms:px-2 ms:py-1 ms:gap-y-1">
1
+ <%= tag.div class: class_names(
2
+ "ms:flex-1 ms:flex-col-reverse ms:@md:flex-row ms:grow ms:flex ms:justify-bewteen ms:bg-neutral-50 ms:rounded-t-md ms:gap-y-1",
3
+ "ms:dark:bg-neutral-700 ms:dark:text-neutral-200"
4
+ ) do %>
2
5
  <%= render partial: "marksmith/shared/tabs" %>
3
6
 
4
7
  <%= render partial: "marksmith/shared/action_bar", locals: { name:, disabled: } %>
5
- </div>
8
+ <% end %>
@@ -0,0 +1,17 @@
1
+ de:
2
+ marksmith:
3
+ attach_from_gallery: Aus Galerie hinzufügen
4
+ bold: Fett
5
+ code: Code
6
+ header: Überschrift
7
+ image: Bild
8
+ italic: Kursiv
9
+ link: Link
10
+ markdown_is_supported: Markdown wird unterstützt
11
+ ordered_list: Nummerierte Liste
12
+ preview: Voransicht
13
+ quote: Zitat
14
+ task_list: TODO Liste
15
+ unordered_list: Liste
16
+ upload_files: Dateien uploaden
17
+ write: Schreiben
@@ -4,8 +4,8 @@ module Marksmith
4
4
 
5
5
  initializer "marksmith.view_helpers" do
6
6
  ActiveSupport.on_load :action_view do
7
- require "marksmith/helper"
8
- ActionView::Base.include Marksmith::Helper
7
+ require_relative "../../app/helpers/marksmith/marksmith_helper"
8
+ ActionView::Base.include Marksmith::MarksmithHelper
9
9
 
10
10
  module FormBuilderExtensions
11
11
  def marksmith(*args, **kwargs, &block)
@@ -1,3 +1,3 @@
1
1
  module Marksmith
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marksmith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Marin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-03-10 00:00:00.000000000 Z
11
+ date: 2025-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -37,13 +37,11 @@ files:
37
37
  - app/assets/config/marksmith_manifest.js
38
38
  - app/assets/images/marksmith/svgs/bold.svg
39
39
  - app/assets/images/marksmith/svgs/code.svg
40
- - app/assets/images/marksmith/svgs/color-swatch copy.svg
41
40
  - app/assets/images/marksmith/svgs/color-swatch.svg
42
41
  - app/assets/images/marksmith/svgs/gallery.svg
43
42
  - app/assets/images/marksmith/svgs/header.svg
44
43
  - app/assets/images/marksmith/svgs/image.svg
45
44
  - app/assets/images/marksmith/svgs/italic.svg
46
- - app/assets/images/marksmith/svgs/link copy.svg
47
45
  - app/assets/images/marksmith/svgs/link.svg
48
46
  - app/assets/images/marksmith/svgs/markdown.svg
49
47
  - app/assets/images/marksmith/svgs/ordered-list.svg
@@ -70,6 +68,7 @@ files:
70
68
  - app/frontend/entrypoints/javascript/controllers/index.js
71
69
  - app/frontend/entrypoints/javascript/controllers/list_continuation_controller.js
72
70
  - app/frontend/entrypoints/javascript/controllers/marksmith_controller.js
71
+ - app/helpers/marksmith/marksmith_helper.rb
73
72
  - app/models/marksmith/application_record.rb
74
73
  - app/models/marksmith/editor.rb
75
74
  - app/models/marksmith/renderer.rb
@@ -83,6 +82,7 @@ files:
83
82
  - app/views/marksmith/shared/_rendered_body.html.erb
84
83
  - app/views/marksmith/shared/_tabs.html.erb
85
84
  - app/views/marksmith/shared/_toolbar.html.erb
85
+ - config/locales/marksmith.de.yml
86
86
  - config/locales/marksmith.en.yml
87
87
  - config/routes.rb
88
88
  - config/vite.json
@@ -92,7 +92,6 @@ files:
92
92
  - lib/marksmith/configuration.rb
93
93
  - lib/marksmith/engine.rb
94
94
  - lib/marksmith/fields/markdown_field.rb
95
- - lib/marksmith/helper.rb
96
95
  - lib/marksmith/version.rb
97
96
  - lib/tasks/marksmith_tasks.rake
98
97
  homepage: https://github.com/avo-hq/marksmith
@@ -1,3 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
2
- <path stroke-linecap="round" stroke-linejoin="round" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01"/>
3
- </svg>
@@ -1,3 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true" data-slot="icon">
2
- <path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"/>
3
- </svg>
@@ -1,39 +0,0 @@
1
- module Marksmith
2
- module Helper
3
- def marksmithed(body)
4
- Marksmith::Renderer.new(body:).render
5
- end
6
-
7
- def marksmith_tag(name, **kwargs, &block)
8
- rails_direct_uploads_url = if defined?(ActiveStorage)
9
- main_app.rails_direct_uploads_url
10
- end
11
-
12
- editor = Marksmith::Editor.new(name:, rails_direct_uploads_url:, **kwargs, &block)
13
-
14
- render partial: "marksmith/shared/editor", locals: { name: editor.name, editor: }
15
- end
16
-
17
- def marksmith_asset_tags(*args, **kwargs)
18
- stylesheet_link_tag("marksmith", *args, **kwargs) +
19
- javascript_include_tag("marksmith.esm.js", *args, **kwargs)
20
- end
21
-
22
- def marksmith_button_classes
23
- class_names("ms:flex ms:items:center ms:cursor-pointer ms:py-1 ms:px-1.5 ms:hover:bg-neutral-200 ms:rounded")
24
- end
25
-
26
- def marksmith_toolbar_button(name, **kwargs)
27
- content_tag "md-#{name}", marksmith_toolbar_svg(name), title: t("marksmith.#{name.to_s.gsub("-", "_")}").humanize, class: marksmith_button_classes
28
- end
29
-
30
- def marksmith_toggle_button_classes
31
- class_names(marksmith_button_classes, "ms:bg-neutral-200 ms:border-0 ms:bg-none ms:text-sm ms:hover:bg-neutral-300 ms:uppercase ms:text-xs ms:font-semibold ms:text-neutral-800")
32
- end
33
-
34
- # TODO: maybe inline svgs in the future
35
- def marksmith_toolbar_svg(name)
36
- image_tag asset_path("marksmith/svgs/#{name}.svg"), class: "ms:inline ms:size-4"
37
- end
38
- end
39
- end