admin_suite 0.3.1 → 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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/app/assets/vendor/easymde.min.css +7 -0
  4. data/app/assets/vendor/easymde.min.js +7 -0
  5. data/app/controllers/admin_suite/application_controller.rb +26 -47
  6. data/app/controllers/admin_suite/portals_controller.rb +2 -2
  7. data/app/controllers/admin_suite/resources_controller.rb +8 -1
  8. data/app/helpers/admin_suite/base_helper.rb +49 -385
  9. data/app/javascript/controllers/admin_suite/markdown_editor_controller.js +21 -2
  10. data/app/views/admin_suite/panels/_stat.html.erb +10 -0
  11. data/app/views/admin_suite/resources/index.html.erb +7 -77
  12. data/app/views/admin_suite/shared/_pagination.html.erb +74 -0
  13. data/app/views/admin_suite/shared/_sidebar.html.erb +1 -1
  14. data/app/views/layouts/admin_suite/application.html.erb +5 -3
  15. data/lib/admin/base/action_executor.rb +19 -51
  16. data/lib/admin/base/resource.rb +53 -14
  17. data/lib/admin_suite/configuration.rb +28 -3
  18. data/lib/admin_suite/definition_loader.rb +194 -0
  19. data/lib/admin_suite/deprecation.rb +48 -0
  20. data/lib/admin_suite/engine.rb +55 -76
  21. data/lib/admin_suite/host_autoload_policy.rb +132 -0
  22. data/lib/admin_suite/legacy_custom_renderer_procs.rb +29 -0
  23. data/lib/admin_suite/portal_definition.rb +11 -0
  24. data/lib/admin_suite/renderer.rb +133 -0
  25. data/lib/admin_suite/renderer_registry.rb +70 -0
  26. data/lib/admin_suite/renderers/code_renderer.rb +15 -0
  27. data/lib/admin_suite/renderers/json_renderer.rb +15 -0
  28. data/lib/admin_suite/renderers/key_value_renderer.rb +39 -0
  29. data/lib/admin_suite/renderers/legacy_gleania.rb +230 -0
  30. data/lib/admin_suite/renderers/table_from_renderer.rb +22 -0
  31. data/lib/admin_suite/section_definition.rb +42 -0
  32. data/lib/admin_suite/ui/field_renderer_registry.rb +31 -4
  33. data/lib/admin_suite/ui/form_field_renderer.rb +1 -7
  34. data/lib/admin_suite/ui/show_formatter_registry.rb +9 -0
  35. data/lib/admin_suite/ui/show_value_formatter.rb +7 -3
  36. data/lib/admin_suite/version.rb +1 -1
  37. data/lib/admin_suite.rb +37 -0
  38. data/lib/generators/admin_suite/install/templates/admin_suite.rb +0 -4
  39. data/test/controllers/resources_controller_test.rb +76 -1
  40. data/test/integration/layout_assets_test.rb +112 -0
  41. data/test/integration/navigation_sections_test.rb +41 -0
  42. data/test/integration/pagination_and_stats_test.rb +152 -0
  43. data/test/lib/action_executor_redirect_test.rb +41 -0
  44. data/test/lib/builtin_renderers_test.rb +202 -0
  45. data/test/lib/definition_loader_test.rb +264 -0
  46. data/test/lib/engine_defaults_test.rb +39 -0
  47. data/test/lib/form_field_renderer_test.rb +64 -0
  48. data/test/lib/legacy_renderer_deprecation_test.rb +35 -0
  49. data/test/lib/renderer_test.rb +221 -0
  50. data/test/lib/resource_exportable_deprecation_test.rb +39 -0
  51. data/test/lib/show_value_formatter_test.rb +88 -0
  52. data/test/lib/zeitwerk_integration_test.rb +28 -64
  53. data/test/test_helper.rb +77 -0
  54. metadata +28 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9bafe4fb74ddbd5bb365501f185970b270950000aeae6e5d9689f7fcf7f32c5b
4
- data.tar.gz: 2490ec0b676767b12fba85bc83f36284cde983ce07008e070d0aec31d882f8e0
3
+ metadata.gz: 816ba44b1d87d7cc1ca92ccfcff24dc15a3c5eaadc23679de61a1977ab007a26
4
+ data.tar.gz: 89690a043f1fdb3e9a175d6fa32da6ba8b1d605c46b631ac23dbac6c9b04f630
5
5
  SHA512:
6
- metadata.gz: 1628a85ae6f41e1ac6d467dace94314b9db51b27e3e044938c9cde52e7ea616b3c5604b23cb694259a8eb828a321e52eb6981852d956c7a0956f8ba5bec20b1e
7
- data.tar.gz: d6cb87908af3d7e20445f2b680bfecb92a0aed93736a5d1c7810f4efa85b5fda85dee4a98c585b765788e6d52950720122b3adf7575c28a68c45e982b9597acb
6
+ metadata.gz: 9aec218fbb03dbb9550bd127eb38aab19709bf13a1874c186a878e3b8f32c4618b0698d879a5d685ae105146a9d4e8c446e4e2e3533c253d40f551b6f00597a9
7
+ data.tar.gz: 63f991346017892d5f0b2e10327cbedc5c86ba2739a5dfb9680c525d62c458a64b2341fc5a0861313391b7898946c5073152ac6ef5182ebfeb3ea2955957a604
data/CHANGELOG.md CHANGED
@@ -7,6 +7,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.0] - 2026-08-01
11
+
12
+ ### Added
13
+ - `AdminSuite::Renderer` base class for show-panel renderers, with shared
14
+ primitives (`json_block`, `code_block`, `key_value_list`, `data_table`,
15
+ `badge`, `empty_state`). Host renderers live in `app/admin/renderers/*.rb`
16
+ as `Admin::Renderers::<Key>Renderer` and are autoloaded from a panel's
17
+ `render: :<key>` option.
18
+ - Built-in renderers `:json`, `:key_value`, `:table_from` and `:code`, usable
19
+ from any resource with no host code. See
20
+ `../_vault/products/admin_suite/docs/renderers.md`.
21
+ - Explicit renderer registration via `AdminSuite::RendererRegistry.register`.
22
+ - Sections are first-class: `AdminSuite.portal :ops do section(:runs) { label "Runs"; order 10 } end`.
23
+ Undeclared sections keep the previous humanized-label, alphabetical-order behavior.
24
+ - `BigDecimal` show-value formatting.
25
+
26
+ ### Changed (BREAKING)
27
+ - `config.portals = {}` now suppresses the gem's built-in default portals.
28
+ Previously `{}` was `blank?` and the defaults re-applied, giving hosts that
29
+ cleared portals four phantom nav entries.
30
+ - Removed `config.tailwind_cdn` (no consumers) and `ColumnDefinition`'s
31
+ `render:` option (never read).
32
+ - Actions returning a persisted record now derive a redirect for any action,
33
+ not only one named `:duplicate`.
34
+ - Association-panel pagination now renders through the same partial as index
35
+ pagination, which has richer markup than the old association-only
36
+ implementation — a visual change for hosts using paginated association
37
+ panels.
38
+ - Unknown form field types now render a plain text field via a registry
39
+ default, the same fallback `:text`/`:string` already used, instead of a
40
+ separate legacy fallback path. Recognized types are unaffected.
41
+
42
+ ### Deprecated
43
+ - `config.custom_renderers[:key] = ->(record, view) {}` procs — migrate to
44
+ `AdminSuite::Renderer` subclasses. Procs keep working in 0.4.x and take
45
+ precedence over a renderer class registered under the same key, so hosts
46
+ can migrate one panel at a time. See
47
+ `../_vault/products/admin_suite/docs/renderers.md`. Now warns once per
48
+ key per process when a legacy proc is used — previously deprecated on
49
+ paper only, with no runtime signal.
50
+ - The built-in `:prompt_template_preview`, `:messages_preview`,
51
+ `:tool_args_preview` and `:turn_messages_preview` renderers. They warn once
52
+ per key per process and are **removed in 0.5.0**.
53
+ - `Resource.exportable(*formats)` — a no-op that warns once per resource
54
+ class, naming the class, and is **removed in 0.5.0**. It was write-only
55
+ in every prior release (never had a reader, never drove any export
56
+ behavior); calling it is now harmless instead of raising `NoMethodError`
57
+ at resource-definition time.
58
+
59
+ ### Fixed
60
+ - A host `Admin::Renderers::<Key>Renderer` class (or an explicit
61
+ `RendererRegistry.register` call) could be silently shadowed by the
62
+ gem's own built-in/deprecated-Gleania registrations under the same key,
63
+ defeating the deprecation advice that recommends defining exactly such a
64
+ class. `RendererRegistry` now separates gem defaults
65
+ (`register_default`) from explicit registrations (`register`);
66
+ `render_custom_section`'s precedence is: legacy `config.custom_renderers`
67
+ proc, explicit registration, host renderer class, gem default.
68
+ - A bare `rescue ActiveRecord::RecordNotFound` in `ResourcesController#set_resource`
69
+ raised `NameError` while handling the real exception in a host without
70
+ ActiveRecord loaded, masking it (same failure class as the AASM fix
71
+ below). Three `object.is_a?(ActiveRecord::Base)` call sites in
72
+ `BaseHelper` had the same problem in its plain-crash form (not masking,
73
+ just a 500) and are now guarded by a shared `active_record_base?`
74
+ predicate.
75
+ - The engine no longer force-includes
76
+ `Internal::Developer::CustomRenderersHelper` into every view. Verified
77
+ non-breaking: a host's `helper :all` (the Rails default) still supplies
78
+ it.
79
+ - EasyMDE is vendored instead of loaded from a CDN (strict-CSP and
80
+ air-gapped hosts).
81
+ - A bare `AASM::InvalidTransition` rescue raised `NameError` during exception
82
+ handling in hosts without AASM installed, masking the original error.
83
+ - Index stat cards no longer emit dynamic Tailwind classes the content
84
+ scanner cannot see.
85
+ - Index and association-panel pagination were two divergent implementations;
86
+ now one partial.
87
+ - Definition files (resources/actions/portals/dashboards) that raise during
88
+ load now surface the error immediately in development and test, instead of
89
+ being silently retried — the old `rescue NameError; retry` could infinite-loop
90
+ in production when a require legitimately failed.
91
+
10
92
  ## [0.3.1] - 2026-08-01
11
93
 
12
94
  ### Fixed
@@ -0,0 +1,7 @@
1
+ /**
2
+ * easymde v2.18.0
3
+ * Copyright Jeroen Akkerman
4
+ * @link https://github.com/ionaru/easy-markdown-editor
5
+ * @license MIT
6
+ */
7
+ .CodeMirror{font-family:monospace;height:300px;color:#000;direction:ltr}.CodeMirror-lines{padding:4px 0}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{padding:0 4px}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:#fff}.CodeMirror-gutters{border-right:1px solid #ddd;background-color:#f7f7f7;white-space:nowrap}.CodeMirror-linenumber{padding:0 3px 0 5px;min-width:20px;text-align:right;color:#999;white-space:nowrap}.CodeMirror-guttermarker{color:#000}.CodeMirror-guttermarker-subtle{color:#999}.CodeMirror-cursor{border-left:1px solid #000;border-right:none;width:0}.CodeMirror div.CodeMirror-secondarycursor{border-left:1px solid silver}.cm-fat-cursor .CodeMirror-cursor{width:auto;border:0!important;background:#7e7}.cm-fat-cursor div.CodeMirror-cursors{z-index:1}.cm-fat-cursor .CodeMirror-line::selection,.cm-fat-cursor .CodeMirror-line>span::selection,.cm-fat-cursor .CodeMirror-line>span>span::selection{background:0 0}.cm-fat-cursor .CodeMirror-line::-moz-selection,.cm-fat-cursor .CodeMirror-line>span::-moz-selection,.cm-fat-cursor .CodeMirror-line>span>span::-moz-selection{background:0 0}.cm-fat-cursor{caret-color:transparent}@-moz-keyframes blink{50%{background-color:transparent}}@-webkit-keyframes blink{50%{background-color:transparent}}@keyframes blink{50%{background-color:transparent}}.cm-tab{display:inline-block;text-decoration:inherit}.CodeMirror-rulers{position:absolute;left:0;right:0;top:-50px;bottom:0;overflow:hidden}.CodeMirror-ruler{border-left:1px solid #ccc;top:0;bottom:0;position:absolute}.cm-s-default .cm-header{color:#00f}.cm-s-default .cm-quote{color:#090}.cm-negative{color:#d44}.cm-positive{color:#292}.cm-header,.cm-strong{font-weight:700}.cm-em{font-style:italic}.cm-link{text-decoration:underline}.cm-strikethrough{text-decoration:line-through}.cm-s-default .cm-keyword{color:#708}.cm-s-default .cm-atom{color:#219}.cm-s-default .cm-number{color:#164}.cm-s-default .cm-def{color:#00f}.cm-s-default .cm-variable-2{color:#05a}.cm-s-default .cm-type,.cm-s-default .cm-variable-3{color:#085}.cm-s-default .cm-comment{color:#a50}.cm-s-default .cm-string{color:#a11}.cm-s-default .cm-string-2{color:#f50}.cm-s-default .cm-meta{color:#555}.cm-s-default .cm-qualifier{color:#555}.cm-s-default .cm-builtin{color:#30a}.cm-s-default .cm-bracket{color:#997}.cm-s-default .cm-tag{color:#170}.cm-s-default .cm-attribute{color:#00c}.cm-s-default .cm-hr{color:#999}.cm-s-default .cm-link{color:#00c}.cm-s-default .cm-error{color:red}.cm-invalidchar{color:red}.CodeMirror-composing{border-bottom:2px solid}div.CodeMirror span.CodeMirror-matchingbracket{color:#0b0}div.CodeMirror span.CodeMirror-nonmatchingbracket{color:#a22}.CodeMirror-matchingtag{background:rgba(255,150,0,.3)}.CodeMirror-activeline-background{background:#e8f2ff}.CodeMirror{position:relative;overflow:hidden;background:#fff}.CodeMirror-scroll{overflow:scroll!important;margin-bottom:-50px;margin-right:-50px;padding-bottom:50px;height:100%;outline:0;position:relative;z-index:0}.CodeMirror-sizer{position:relative;border-right:50px solid transparent}.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar{position:absolute;z-index:6;display:none;outline:0}.CodeMirror-vscrollbar{right:0;top:0;overflow-x:hidden;overflow-y:scroll}.CodeMirror-hscrollbar{bottom:0;left:0;overflow-y:hidden;overflow-x:scroll}.CodeMirror-scrollbar-filler{right:0;bottom:0}.CodeMirror-gutter-filler{left:0;bottom:0}.CodeMirror-gutters{position:absolute;left:0;top:0;min-height:100%;z-index:3}.CodeMirror-gutter{white-space:normal;height:100%;display:inline-block;vertical-align:top;margin-bottom:-50px}.CodeMirror-gutter-wrapper{position:absolute;z-index:4;background:0 0!important;border:none!important}.CodeMirror-gutter-background{position:absolute;top:0;bottom:0;z-index:4}.CodeMirror-gutter-elt{position:absolute;cursor:default;z-index:4}.CodeMirror-gutter-wrapper ::selection{background-color:transparent}.CodeMirror-gutter-wrapper ::-moz-selection{background-color:transparent}.CodeMirror-lines{cursor:text;min-height:1px}.CodeMirror pre.CodeMirror-line,.CodeMirror pre.CodeMirror-line-like{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0;border-width:0;background:0 0;font-family:inherit;font-size:inherit;margin:0;white-space:pre;word-wrap:normal;line-height:inherit;color:inherit;z-index:2;position:relative;overflow:visible;-webkit-tap-highlight-color:transparent;-webkit-font-variant-ligatures:contextual;font-variant-ligatures:contextual}.CodeMirror-wrap pre.CodeMirror-line,.CodeMirror-wrap pre.CodeMirror-line-like{word-wrap:break-word;white-space:pre-wrap;word-break:normal}.CodeMirror-linebackground{position:absolute;left:0;right:0;top:0;bottom:0;z-index:0}.CodeMirror-linewidget{position:relative;z-index:2;padding:.1px}.CodeMirror-rtl pre{direction:rtl}.CodeMirror-code{outline:0}.CodeMirror-gutter,.CodeMirror-gutters,.CodeMirror-linenumber,.CodeMirror-scroll,.CodeMirror-sizer{-moz-box-sizing:content-box;box-sizing:content-box}.CodeMirror-measure{position:absolute;width:100%;height:0;overflow:hidden;visibility:hidden}.CodeMirror-cursor{position:absolute;pointer-events:none}.CodeMirror-measure pre{position:static}div.CodeMirror-cursors{visibility:hidden;position:relative;z-index:3}div.CodeMirror-dragcursors{visibility:visible}.CodeMirror-focused div.CodeMirror-cursors{visibility:visible}.CodeMirror-selected{background:#d9d9d9}.CodeMirror-focused .CodeMirror-selected{background:#d7d4f0}.CodeMirror-crosshair{cursor:crosshair}.CodeMirror-line::selection,.CodeMirror-line>span::selection,.CodeMirror-line>span>span::selection{background:#d7d4f0}.CodeMirror-line::-moz-selection,.CodeMirror-line>span::-moz-selection,.CodeMirror-line>span>span::-moz-selection{background:#d7d4f0}.cm-searching{background-color:#ffa;background-color:rgba(255,255,0,.4)}.cm-force-border{padding-right:.1px}@media print{.CodeMirror div.CodeMirror-cursors{visibility:hidden}}.cm-tab-wrap-hack:after{content:''}span.CodeMirror-selectedtext{background:0 0}.EasyMDEContainer{display:block}.CodeMirror-rtl pre{direction:rtl}.EasyMDEContainer.sided--no-fullscreen{display:flex;flex-direction:row;flex-wrap:wrap}.EasyMDEContainer .CodeMirror{box-sizing:border-box;height:auto;border:1px solid #ced4da;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px;font:inherit;z-index:0;word-wrap:break-word}.EasyMDEContainer .CodeMirror-scroll{cursor:text}.EasyMDEContainer .CodeMirror-fullscreen{background:#fff;position:fixed!important;top:50px;left:0;right:0;bottom:0;height:auto;z-index:8;border-right:none!important;border-bottom-right-radius:0!important}.EasyMDEContainer .CodeMirror-sided{width:50%!important}.EasyMDEContainer.sided--no-fullscreen .CodeMirror-sided{border-right:none!important;border-bottom-right-radius:0;position:relative;flex:1 1 auto}.EasyMDEContainer .CodeMirror-placeholder{opacity:.5}.EasyMDEContainer .CodeMirror-focused .CodeMirror-selected{background:#d9d9d9}.editor-toolbar{position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none;padding:9px 10px;border-top:1px solid #ced4da;border-left:1px solid #ced4da;border-right:1px solid #ced4da;border-top-left-radius:4px;border-top-right-radius:4px}.editor-toolbar.fullscreen{width:100%;height:50px;padding-top:10px;padding-bottom:10px;box-sizing:border-box;background:#fff;border:0;position:fixed;top:0;left:0;opacity:1;z-index:9}.editor-toolbar.fullscreen::before{width:20px;height:50px;background:-moz-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,#fff),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,#fff 0,rgba(255,255,255,0) 100%);background:linear-gradient(to right,#fff 0,rgba(255,255,255,0) 100%);position:fixed;top:0;left:0;margin:0;padding:0}.editor-toolbar.fullscreen::after{width:20px;height:50px;background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(100%,#fff));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,#fff 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,#fff 100%);position:fixed;top:0;right:0;margin:0;padding:0}.EasyMDEContainer.sided--no-fullscreen .editor-toolbar{width:100%}.editor-toolbar .easymde-dropdown,.editor-toolbar button{background:0 0;display:inline-block;text-align:center;text-decoration:none!important;height:30px;margin:0;padding:0;border:1px solid transparent;border-radius:3px;cursor:pointer}.editor-toolbar button{font-weight:700;min-width:30px;padding:0 6px;white-space:nowrap}.editor-toolbar button.active,.editor-toolbar button:hover{background:#fcfcfc;border-color:#95a5a6}.editor-toolbar i.separator{display:inline-block;width:0;border-left:1px solid #d9d9d9;border-right:1px solid #fff;color:transparent;text-indent:-10px;margin:0 6px}.editor-toolbar button:after{font-family:Arial,"Helvetica Neue",Helvetica,sans-serif;font-size:65%;vertical-align:text-bottom;position:relative;top:2px}.editor-toolbar button.heading-1:after{content:"1"}.editor-toolbar button.heading-2:after{content:"2"}.editor-toolbar button.heading-3:after{content:"3"}.editor-toolbar button.heading-bigger:after{content:"▲"}.editor-toolbar button.heading-smaller:after{content:"▼"}.editor-toolbar.disabled-for-preview button:not(.no-disable){opacity:.6;pointer-events:none}@media only screen and (max-width:700px){.editor-toolbar i.no-mobile{display:none}}.editor-statusbar{padding:8px 10px;font-size:12px;color:#959694;text-align:right}.EasyMDEContainer.sided--no-fullscreen .editor-statusbar{width:100%}.editor-statusbar span{display:inline-block;min-width:4em;margin-left:1em}.editor-statusbar .lines:before{content:'lines: '}.editor-statusbar .words:before{content:'words: '}.editor-statusbar .characters:before{content:'characters: '}.editor-preview-full{position:absolute;width:100%;height:100%;top:0;left:0;z-index:7;overflow:auto;display:none;box-sizing:border-box}.editor-preview-side{position:fixed;bottom:0;width:50%;top:50px;right:0;z-index:9;overflow:auto;display:none;box-sizing:border-box;border:1px solid #ddd;word-wrap:break-word}.editor-preview-active-side{display:block}.EasyMDEContainer.sided--no-fullscreen .editor-preview-active-side{flex:1 1 auto;height:auto;position:static}.editor-preview-active{display:block}.editor-preview{padding:10px;background:#fafafa}.editor-preview>p{margin-top:0}.editor-preview pre{background:#eee;margin-bottom:10px}.editor-preview table td,.editor-preview table th{border:1px solid #ddd;padding:5px}.cm-s-easymde .cm-tag{color:#63a35c}.cm-s-easymde .cm-attribute{color:#795da3}.cm-s-easymde .cm-string{color:#183691}.cm-s-easymde .cm-header-1{font-size:calc(1.375rem + 1.5vw)}.cm-s-easymde .cm-header-2{font-size:calc(1.325rem + .9vw)}.cm-s-easymde .cm-header-3{font-size:calc(1.3rem + .6vw)}.cm-s-easymde .cm-header-4{font-size:calc(1.275rem + .3vw)}.cm-s-easymde .cm-header-5{font-size:1.25rem}.cm-s-easymde .cm-header-6{font-size:1rem}.cm-s-easymde .cm-header-1,.cm-s-easymde .cm-header-2,.cm-s-easymde .cm-header-3,.cm-s-easymde .cm-header-4,.cm-s-easymde .cm-header-5,.cm-s-easymde .cm-header-6{margin-bottom:.5rem;line-height:1.2}.cm-s-easymde .cm-comment{background:rgba(0,0,0,.05);border-radius:2px}.cm-s-easymde .cm-link{color:#7f8c8d}.cm-s-easymde .cm-url{color:#aab2b3}.cm-s-easymde .cm-quote{color:#7f8c8d;font-style:italic}.editor-toolbar .easymde-dropdown{position:relative;background:linear-gradient(to bottom right,#fff 0,#fff 84%,#333 50%,#333 100%);border-radius:0;border:1px solid #fff}.editor-toolbar .easymde-dropdown:hover{background:linear-gradient(to bottom right,#fff 0,#fff 84%,#333 50%,#333 100%)}.easymde-dropdown-content{display:block;visibility:hidden;position:absolute;background-color:#f9f9f9;box-shadow:0 8px 16px 0 rgba(0,0,0,.2);padding:8px;z-index:2;top:30px}.easymde-dropdown:active .easymde-dropdown-content,.easymde-dropdown:focus .easymde-dropdown-content,.easymde-dropdown:focus-within .easymde-dropdown-content{visibility:visible}.easymde-dropdown-content button{display:block}span[data-img-src]::after{content:'';background-image:var(--bg-image);display:block;max-height:100%;max-width:100%;background-size:contain;height:0;padding-top:var(--height);width:var(--width);background-repeat:no-repeat}.CodeMirror .cm-spell-error:not(.cm-url):not(.cm-comment):not(.cm-tag):not(.cm-word){background:rgba(255,0,0,.15)}