marksmith 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +60 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/config/marksmith_manifest.js +2 -0
  6. data/app/assets/images/marksmith/svgs/bold.svg +3 -0
  7. data/app/assets/images/marksmith/svgs/code.svg +3 -0
  8. data/app/assets/images/marksmith/svgs/color-swatch copy.svg +3 -0
  9. data/app/assets/images/marksmith/svgs/color-swatch.svg +3 -0
  10. data/app/assets/images/marksmith/svgs/heading.svg +1 -0
  11. data/app/assets/images/marksmith/svgs/italic.svg +3 -0
  12. data/app/assets/images/marksmith/svgs/link copy.svg +3 -0
  13. data/app/assets/images/marksmith/svgs/link.svg +3 -0
  14. data/app/assets/images/marksmith/svgs/list-bullet.svg +3 -0
  15. data/app/assets/images/marksmith/svgs/list-todo.svg +1 -0
  16. data/app/assets/images/marksmith/svgs/numbered-list.svg +3 -0
  17. data/app/assets/images/marksmith/svgs/photo.svg +3 -0
  18. data/app/assets/images/marksmith/svgs/quote.svg +1 -0
  19. data/app/assets/stylesheets/marksmith/application.css +15 -0
  20. data/app/assets/stylesheets/marksmith.css +517 -0
  21. data/app/controllers/marksmith/application_controller.rb +4 -0
  22. data/app/controllers/marksmith/markdown_previews_controller.rb +8 -0
  23. data/app/frontend/entrypoints/application.css +6 -0
  24. data/app/frontend/entrypoints/application.js +29 -0
  25. data/app/frontend/entrypoints/javascript/controllers/application.js +9 -0
  26. data/app/frontend/entrypoints/javascript/controllers/index.js +7 -0
  27. data/app/frontend/entrypoints/javascript/controllers/marksmith_controller.js +111 -0
  28. data/app/helpers/marksmith/application_helper.rb +4 -0
  29. data/app/helpers/marksmith/helper.rb +21 -0
  30. data/app/jobs/marksmith/application_job.rb +4 -0
  31. data/app/mailers/marksmith/application_mailer.rb +6 -0
  32. data/app/models/marksmith/application_record.rb +5 -0
  33. data/app/models/marksmith/renderer.rb +23 -0
  34. data/app/views/layouts/marksmith/application.html.erb +17 -0
  35. data/app/views/marksmith/markdown_previews/create.turbo_stream.erb +6 -0
  36. data/app/views/marksmith/shared/_editor.html.erb +120 -0
  37. data/app/views/marksmith/shared/_rendered_body.html.erb +3 -0
  38. data/config/locales/marksmith.en.yml +33 -0
  39. data/config/routes.rb +3 -0
  40. data/config/vite.json +16 -0
  41. data/lib/marksmith/engine.rb +25 -0
  42. data/lib/marksmith/version.rb +3 -0
  43. data/lib/marksmith.rb +5 -0
  44. data/lib/tasks/marksmith_tasks.rake +4 -0
  45. metadata +131 -0
@@ -0,0 +1,111 @@
1
+ /* eslint-disable camelcase */
2
+ import '@github/markdown-toolbar-element'
3
+ import { Controller } from '@hotwired/stimulus'
4
+ import { DirectUpload } from '@rails/activestorage'
5
+ import { post } from '@rails/request.js'
6
+ import { subscribe } from '@github/paste-markdown'
7
+
8
+ // upload code from Jeremy Smith's blog post
9
+ // https://hybrd.co/posts/github-issue-style-file-uploader-using-stimulus-and-active-storage
10
+
11
+ // Connects to data-controller="marksmith"
12
+ export default class extends Controller {
13
+ static values = {
14
+ attachUrl: String,
15
+ previewUrl: String,
16
+ resourceClass: String,
17
+ fieldId: String,
18
+ }
19
+
20
+ static targets = ['fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
21
+
22
+ connect() {
23
+ subscribe(this.fieldElementTarget, { defaultPlainTextPaste: { urlLinks: true } })
24
+ }
25
+
26
+ switchToWrite(event) {
27
+ event.preventDefault()
28
+
29
+ // toggle buttons
30
+ this.writeTabButtonTarget.classList.add('ms:hidden')
31
+ this.previewTabButtonTarget.classList.remove('ms:hidden')
32
+
33
+ // toggle write/preview buttons
34
+ this.fieldElementTarget.classList.remove('ms:hidden')
35
+ this.previewElementTarget.classList.add('ms:hidden')
36
+
37
+ // toggle the toolbar back
38
+ this.toolbarTarget.classList.remove('ms:hidden')
39
+ }
40
+
41
+ switchToPreview(event) {
42
+ event.preventDefault()
43
+
44
+ post(this.previewUrlValue, {
45
+ body: {
46
+ body: this.fieldElementTarget.value,
47
+ resource_class: this.resourceClassValue,
48
+ field_id: this.fieldIdValue,
49
+ element_id: this.previewElementTarget.id,
50
+ },
51
+ responseKind: 'turbo-stream',
52
+ })
53
+
54
+ // set the min height to the field element height
55
+ this.previewElementTarget.style.minHeight = `${this.fieldElementTarget.offsetHeight}px`
56
+
57
+ // toggle buttons
58
+ this.writeTabButtonTarget.classList.remove('ms:hidden')
59
+ this.previewTabButtonTarget.classList.add('ms:hidden')
60
+
61
+ // toggle elements
62
+ this.fieldElementTarget.classList.add('ms:hidden')
63
+ this.previewElementTarget.classList.remove('ms:hidden')
64
+
65
+ // toggle the toolbar
66
+ this.toolbarTarget.classList.add('ms:hidden')
67
+ }
68
+
69
+ dropUpload(event) {
70
+ event.preventDefault()
71
+ this.uploadFiles(event.dataTransfer.files)
72
+ }
73
+
74
+ pasteUpload(event) {
75
+ if (!event.clipboardData.files.length) return
76
+
77
+ event.preventDefault()
78
+ this.uploadFiles(event.clipboardData.files)
79
+ }
80
+
81
+ uploadFiles(files) {
82
+ Array.from(files).forEach((file) => this.uploadFile(file))
83
+ }
84
+
85
+ uploadFile(file) {
86
+ const upload = new DirectUpload(file, this.attachUrlValue)
87
+
88
+ upload.create((error, blob) => {
89
+ if (error) {
90
+ console.log('Error', error)
91
+ } else {
92
+ const text = this.markdownLink(blob)
93
+ const start = this.fieldElementTarget.selectionStart
94
+ const end = this.fieldElementTarget.selectionEnd
95
+ this.fieldElementTarget.setRangeText(text, start, end)
96
+ }
97
+ })
98
+ }
99
+
100
+ markdownLink(blob) {
101
+ const { filename } = blob
102
+ const url = `/rails/active_storage/blobs/${blob.signed_id}/${filename}`
103
+ const prefix = (this.isImage(blob.content_type) ? '!' : '')
104
+
105
+ return `${prefix}[${filename}](${url})\n`
106
+ }
107
+
108
+ isImage(contentType) {
109
+ return ['image/jpeg', 'image/gif', 'image/png'].includes(contentType)
110
+ }
111
+ }
@@ -0,0 +1,4 @@
1
+ module Marksmith
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,21 @@
1
+ module Marksmith
2
+ module Helper
3
+ def marksmithed(body)
4
+ Marksmith::Renderer.new.renderer.render(body)
5
+ end
6
+
7
+ def marksmith_tag(name, **kwargs, &block)
8
+ render partial: "marksmith/shared/editor", locals: { name: name, **kwargs }
9
+ end
10
+
11
+ def marksmith_asset_tags(*args, **kwargs)
12
+ stylesheet_link_tag("marksmith", *args, **kwargs) +
13
+ javascript_include_tag("marksmith.esm.js", *args, **kwargs)
14
+ end
15
+
16
+ # TODO: maybe inline svgs in the future
17
+ def svg(name, options = {})
18
+ image_tag asset_path("marksmith/svgs/#{name}.svg"), options
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ module Marksmith
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Marksmith
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: "from@example.com"
4
+ layout "mailer"
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Marksmith
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ require "redcarpet"
2
+ require "rouge"
3
+
4
+ module Marksmith
5
+ class Renderer
6
+ def renderer
7
+ ::Redcarpet::Markdown.new(
8
+ ::Redcarpet::Render::HTML,
9
+ tables: true,
10
+ lax_spacing: true,
11
+ fenced_code_blocks: true,
12
+ space_after_headers: true,
13
+ hard_wrap: true,
14
+ autolink: true,
15
+ strikethrough: true,
16
+ underline: true,
17
+ highlight: true,
18
+ quote: true,
19
+ with_toc_data: true
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Marksmith</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "marksmith/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
@@ -0,0 +1,6 @@
1
+ <%= turbo_stream.update params[:element_id] do %>
2
+ <div class="ms:px-3 ms:py-2">
3
+ <%= render "marksmith/shared/rendered_body", body: @body %>
4
+ </div>
5
+ <% end %>
6
+
@@ -0,0 +1,120 @@
1
+ <%
2
+ data_attributes = local_assigns[:data] || {}
3
+ disabled = local_assigns[:disabled] || false
4
+ placeholder = local_assigns[:placeholder] || nil
5
+ autofocus = local_assigns[:autofocus] || false
6
+ style = local_assigns[:style] || nil
7
+ classes = local_assigns[:classes] || nil
8
+ rows = local_assigns[:rows] || 15
9
+ %>
10
+ <%= content_tag :div,
11
+ class: "ms:flex ms:flex-col ms:w-full ms:border ms:border-zinc-300 ms:rounded ms:@container ms:group ms:focus-within:border-zinc-500",
12
+ data: {
13
+ controller: "marksmith",
14
+ marksmith_preview_url_value: marksmith.markdown_previews_path,
15
+ marksmith_active_tab_class: "bg-white",
16
+ marksmith_attach_url_value: rails_direct_uploads_url,
17
+ marksmith_resource_class_value: @resource.class.name,
18
+ marksmith_field_id_value: name,
19
+ } do %>
20
+ <% button_classes = class_names("ms:flex ms:items:center ms:cursor-pointer ms:py-1 ms:px-1.5 ms:hover:bg-zinc-200 ms:rounded") %>
21
+ <% toggle_button_classes = class_names(button_classes, "ms:border-0 ms:bg-none") %>
22
+ <div class="ms:flex-1 ms:flex-col-reverse ms:@md:flex-row ms:grow ms:flex ms:justify-bewteen ms:bg-zinc-50 ms:rounded ms:px-2 ms:py-1 ms:gap-y-1">
23
+ <div class="ms:flex-1 ms:flex ms:items:center">
24
+ <button class="<%= toggle_button_classes %>" data-action="click->marksmith#switchToPreview" data-marksmith-target="previewTabButton">
25
+ <%= t('marksmith.preview').humanize %>
26
+ </button>
27
+ <button class="<%= toggle_button_classes %> ms:hidden ms:bg-zinc-200" data-action="click->marksmith#switchToWrite" data-marksmith-target="writeTabButton">
28
+ <%= t('marksmith.write').humanize %>
29
+ </button>
30
+ </div>
31
+
32
+ <markdown-toolbar for="<%= name %>" class="<%= class_names("ms:flex ms:flex-wrap", "ms:pointer-events-none": disabled) %>" data-marksmith-target="toolbar">
33
+ <md-bold
34
+ title="<%= t('marksmith.bold').humanize %>"
35
+ class="<%= button_classes %>"
36
+ >
37
+ <%= svg "bold", class: "ms:inline ms:size-4" %>
38
+ </md-bold>
39
+ <md-header
40
+ title="<%= t('marksmith.heading').humanize %>"
41
+ class="<%= button_classes %>"
42
+ >
43
+ <%= svg "heading", class: "ms:inline ms:size-4" %>
44
+ </md-header>
45
+ <md-italic
46
+ title="<%= t('marksmith.italic').humanize %>"
47
+ class="<%= button_classes %>"
48
+ >
49
+ <%= svg "italic", class: "ms:inline ms:size-4" %>
50
+ </md-italic>
51
+ <md-quote
52
+ title="<%= t('marksmith.quote').humanize %>"
53
+ class="<%= button_classes %>"
54
+ >
55
+ <%= svg "quote", class: "ms:inline ms:size-4" %>
56
+ </md-quote>
57
+ <md-code
58
+ title="<%= t('marksmith.code').humanize %>"
59
+ class="<%= button_classes %>"
60
+ >
61
+ <%= svg "code", class: "ms:inline ms:size-4" %>
62
+ </md-code>
63
+ <md-link
64
+ title="<%= t('marksmith.link').humanize %>"
65
+ class="<%= button_classes %>"
66
+ >
67
+ <%= svg "link", class: "ms:inline ms:size-4" %>
68
+ </md-link>
69
+ <md-image
70
+ title="<%= t('marksmith.image').humanize %>"
71
+ class="<%= button_classes %>"
72
+ >
73
+ <%= svg "photo", class: "ms:inline ms:size-4" %>
74
+ </md-image>
75
+ <md-unordered-list
76
+ title="<%= t('marksmith.unordered_list').humanize %>"
77
+ class="<%= button_classes %>"
78
+ >
79
+ <%= svg "list-bullet", class: "ms:inline ms:size-4" %>
80
+ </md-unordered-list>
81
+ <md-ordered-list
82
+ title="<%= t('marksmith.ordered_list').humanize %>"
83
+ class="<%= button_classes %>"
84
+ >
85
+ <%= svg "numbered-list", class: "ms:inline ms:size-4" %>
86
+ </md-ordered-list>
87
+ <md-task-list
88
+ title="<%= t('marksmith.task_list').humanize %>"
89
+ class="<%= button_classes %>"
90
+ >
91
+ <%= svg "list-todo", class: "ms:inline ms:size-4" %>
92
+ </md-task-list>
93
+ </markdown-toolbar>
94
+ </div>
95
+
96
+ <div class="ms:border-t ms:border-zinc-300 ms:flex">
97
+ <%= form.text_area name,
98
+ id: name,
99
+ # value: @field.value,
100
+ class: class_names("ms:flex ms:flex-1 ms:rounded ms:border-none ms:py-2 ms:px-3 ms:focus:outline-none", classes),
101
+ rows: rows,
102
+ data: {
103
+ marksmith_target: "fieldElement",
104
+ action: "drop->marksmith#dropUpload paste->marksmith#pasteUpload",
105
+ **data_attributes
106
+ },
107
+ disabled:,
108
+ placeholder:,
109
+ autofocus:,
110
+ style:
111
+ %>
112
+ <%= content_tag :div, class: "ms:hidden ms:markdown-preview", id: "markdown-preview-#{name}", data: { marksmith_target: "previewElement" } do %>
113
+ <div class="button-spinner">
114
+ <div class="double-bounce1"></div>
115
+ <div class="double-bounce2"></div>
116
+ </div>
117
+ <% end %>
118
+ </div>
119
+ </div>
120
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= content_tag :div, class: "ms:prose ms:prose-zinc" do %>
2
+ <%= sanitize(@body, tags: %w(table th tr td span) + ActionView::Helpers::SanitizeHelper.sanitizer_vendor.safe_list_sanitizer.allowed_tags.to_a) %>
3
+ <% end %>
@@ -0,0 +1,33 @@
1
+ # Files in the config/locales directory are used for internationalization and
2
+ # are automatically loaded by Rails. If you want to use locales other than
3
+ # English, add the necessary files in this directory.
4
+ #
5
+ # To use the locales, use `I18n.t`:
6
+ #
7
+ # I18n.t "hello"
8
+ #
9
+ # In views, this is aliased to just `t`:
10
+ #
11
+ # <%= t("hello") %>
12
+ #
13
+ # To use a different locale, set it with `I18n.locale`:
14
+ #
15
+ # I18n.locale = :es
16
+ #
17
+ # This would use the information in config/locales/es.yml.
18
+ #
19
+ # To learn more about the API, please read the Rails Internationalization guide
20
+ # at https://guides.rubyonrails.org/i18n.html.
21
+ #
22
+ # Be aware that YAML interprets the following case-insensitive strings as
23
+ # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings
24
+ # must be quoted to be interpreted as strings. For example:
25
+ #
26
+ # en:
27
+ # "yes": yup
28
+ # enabled: "ON"
29
+ # TODO: add keys here
30
+
31
+ en:
32
+ marksmith:
33
+ hello: "Hello world"
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Marksmith::Engine.routes.draw do
2
+ resources :markdown_previews, only: [:create]
3
+ end
data/config/vite.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "all": {
3
+ "sourceCodeDir": "app/frontend",
4
+ "watchAdditionalPaths": []
5
+ },
6
+ "development": {
7
+ "autoBuild": true,
8
+ "publicOutputDir": "vite-dev",
9
+ "port": 3036
10
+ },
11
+ "test": {
12
+ "autoBuild": true,
13
+ "publicOutputDir": "vite-test",
14
+ "port": 3037
15
+ }
16
+ }
@@ -0,0 +1,25 @@
1
+ module Marksmith
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Marksmith
4
+
5
+ initializer "marksmith.view_helpers" do
6
+ ActiveSupport.on_load :action_view do
7
+ ActionView::Base.include Marksmith::Helper
8
+
9
+ module FormBuilderExtensions
10
+ def marksmith(*args, **kwargs, &block)
11
+ @template.marksmith_tag(*args, **kwargs.merge(form: self), &block)
12
+ end
13
+ end
14
+
15
+ ActionView::Helpers::FormBuilder.include FormBuilderExtensions
16
+ end
17
+ end
18
+
19
+ initializer "marksmith.assets.precompile" do |app|
20
+ if Rails.application.config.respond_to?(:assets)
21
+ app.config.assets.precompile << "marksmith_manifest.js"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Marksmith
2
+ VERSION = "0.0.4"
3
+ end
data/lib/marksmith.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "marksmith/version"
2
+ require "marksmith/engine"
3
+
4
+ module Marksmith
5
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :marksmith do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: marksmith
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Adrian Marin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-01-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: redcarpet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rouge
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Marksmith is a GitHub-style markdown editor for Ruby on Rails applications.
56
+ email:
57
+ - adrian@adrianthedev.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - MIT-LICENSE
63
+ - README.md
64
+ - Rakefile
65
+ - app/assets/config/marksmith_manifest.js
66
+ - app/assets/images/marksmith/svgs/bold.svg
67
+ - app/assets/images/marksmith/svgs/code.svg
68
+ - app/assets/images/marksmith/svgs/color-swatch copy.svg
69
+ - app/assets/images/marksmith/svgs/color-swatch.svg
70
+ - app/assets/images/marksmith/svgs/heading.svg
71
+ - app/assets/images/marksmith/svgs/italic.svg
72
+ - app/assets/images/marksmith/svgs/link copy.svg
73
+ - app/assets/images/marksmith/svgs/link.svg
74
+ - app/assets/images/marksmith/svgs/list-bullet.svg
75
+ - app/assets/images/marksmith/svgs/list-todo.svg
76
+ - app/assets/images/marksmith/svgs/numbered-list.svg
77
+ - app/assets/images/marksmith/svgs/photo.svg
78
+ - app/assets/images/marksmith/svgs/quote.svg
79
+ - app/assets/stylesheets/marksmith.css
80
+ - app/assets/stylesheets/marksmith/application.css
81
+ - app/controllers/marksmith/application_controller.rb
82
+ - app/controllers/marksmith/markdown_previews_controller.rb
83
+ - app/frontend/entrypoints/application.css
84
+ - app/frontend/entrypoints/application.js
85
+ - app/frontend/entrypoints/javascript/controllers/application.js
86
+ - app/frontend/entrypoints/javascript/controllers/index.js
87
+ - app/frontend/entrypoints/javascript/controllers/marksmith_controller.js
88
+ - app/helpers/marksmith/application_helper.rb
89
+ - app/helpers/marksmith/helper.rb
90
+ - app/jobs/marksmith/application_job.rb
91
+ - app/mailers/marksmith/application_mailer.rb
92
+ - app/models/marksmith/application_record.rb
93
+ - app/models/marksmith/renderer.rb
94
+ - app/views/layouts/marksmith/application.html.erb
95
+ - app/views/marksmith/markdown_previews/create.turbo_stream.erb
96
+ - app/views/marksmith/shared/_editor.html.erb
97
+ - app/views/marksmith/shared/_rendered_body.html.erb
98
+ - config/locales/marksmith.en.yml
99
+ - config/routes.rb
100
+ - config/vite.json
101
+ - lib/marksmith.rb
102
+ - lib/marksmith/engine.rb
103
+ - lib/marksmith/version.rb
104
+ - lib/tasks/marksmith_tasks.rake
105
+ homepage: https://github.com/avo-hq/marksmith
106
+ licenses:
107
+ - MIT
108
+ metadata:
109
+ homepage_uri: https://github.com/avo-hq/marksmith
110
+ source_code_uri: https://github.com/avo-hq/marksmith
111
+ changelog_uri: https://github.com/avo-hq/marksmith/releases
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubygems_version: 3.4.10
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Marksmith is a GitHub-style markdown editor for Ruby on Rails applications.
131
+ test_files: []