panda-editor 0.3.0 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3b8232a60a069137aa8a04c6611f0583877cf036ba6c62c82bef72584c5216d
4
- data.tar.gz: 975f2cb5e0fb8ce6eaa5ae8eb80e3135e1ff44126c102271dd318b148b40b47a
3
+ metadata.gz: fbf5326beb171bbb5448b7545bfe87fdef49faac3c82f85c1a64a30cc7aa276d
4
+ data.tar.gz: 05c314fcf2d2b2c5cdfe2d4e2684b392bc2d4cd7231c91870a6f6100a1ca7170
5
5
  SHA512:
6
- metadata.gz: a012d400a6af140737018adb0187ce7e54be0614d1fd833492cebb35d4310aac91eeab2109ab8d08857333226624084d2ada8a567c21ff8ea43fff018ca17c0c
7
- data.tar.gz: 8df13bfa16acea6e9e65b22e20ff3e138791648c654090d1134140fa94b1bea7b8d439ef8544a73c9c7ac5ad8066539e9eca1842e8f152d86e3634b65bb71c73
6
+ metadata.gz: cab18e487c3e4021cebf6ffbd7c98a4c15103ca96a53cbbbd97b3b377a99d83d7c81de246f620359317d166b5716b56d3105be01d7ab1930c96a9f103164ac1b
7
+ data.tar.gz: 1c3ae8993db2fe42ebd8e4c22c664655aa86961757605d897ca5c4c4553b3c0e4427c203d15b23bfae7ce4d83cfc92d6f02d8aa3dc025f3babb3673bc1850de1
data/CHANGELOG.md CHANGED
@@ -5,6 +5,31 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.0] - 2025-11-04
9
+
10
+ ### Fixed
11
+ - Code quality improvements with Rubocop auto-corrections
12
+ - Fixed 1263 style violations across the codebase
13
+ - Converted double-quoted strings to single quotes where appropriate
14
+ - Improved code consistency and readability
15
+ - Corrected string literal usage throughout
16
+ - Fixed extra spacing and formatting issues
17
+
18
+ ### Changed
19
+ - All tests passing (53 examples, 0 failures)
20
+ - Improved code maintainability
21
+
22
+ ## [0.4.0] - 2025-10-30
23
+
24
+ ### Added
25
+ - Markdown support for rich text formatting in footnotes
26
+ - **Bold**, *italic*, `code`, ~~strikethrough~~, and [link](url) support
27
+ - Automatic URL linking in markdown content
28
+ - Security-hardened Redcarpet configuration (no images, safe links only)
29
+ - Works alongside existing autolink_urls option
30
+ - Comprehensive test coverage for markdown features
31
+ - Updated documentation with markdown examples
32
+
8
33
  ## [0.3.0] - 2025-10-30
9
34
 
10
35
  ### Added
data/README.md CHANGED
@@ -168,6 +168,27 @@ The sources section includes data attributes for integration with JavaScript fra
168
168
 
169
169
  See [docs/FOOTNOTES.md](docs/FOOTNOTES.md) for detailed implementation examples.
170
170
 
171
+ ### Markdown Support
172
+
173
+ Enable markdown formatting in footnote content for rich text citations:
174
+
175
+ ```ruby
176
+ renderer = Panda::Editor::Renderer.new(@content, markdown: true)
177
+ html = renderer.render
178
+ ```
179
+
180
+ Supports **bold**, *italic*, `code`, ~~strikethrough~~, and [links](url):
181
+
182
+ **Input:**
183
+ ```
184
+ Smith, J. (2023). **Important study** on *ADHD treatment*. See https://example.com for details.
185
+ ```
186
+
187
+ **Output:**
188
+ ```html
189
+ Smith, J. (2023). <strong>Important study</strong> on <em>ADHD treatment</em>. See <a href="https://example.com" target="_blank" rel="noopener noreferrer">https://example.com</a> for details.
190
+ ```
191
+
171
192
  ### Auto-linking URLs
172
193
 
173
194
  Enable automatic URL linking in footnote content:
@@ -195,6 +216,9 @@ Features:
195
216
  - Won't double-link URLs already in `<a>` tags
196
217
  - Supports `http://`, `https://`, `ftp://`, and `www.` URLs
197
218
  - Handles multiple URLs in the same footnote
219
+ - Can be combined with `markdown: true` (markdown's autolink runs first, then custom autolink for any remaining URLs)
220
+
221
+ **Note:** When using `markdown: true`, you typically don't need `autolink_urls: true` as markdown includes built-in autolinking. However, both options can work together safely.
198
222
 
199
223
  To enable globally for all content using the `Panda::Editor::Content` concern, pass the option in `generate_cached_content`.
200
224
 
@@ -9,78 +9,78 @@ module Panda
9
9
  return {} if html.blank?
10
10
 
11
11
  # If it's already in EditorJS format, return as is
12
- return html if html.is_a?(Hash) && (html["blocks"].present? || html[:blocks].present?)
12
+ return html if html.is_a?(Hash) && (html['blocks'].present? || html[:blocks].present?)
13
13
 
14
14
  begin
15
15
  # Parse the HTML content
16
16
  doc = Nokogiri::HTML.fragment(html.to_s)
17
- raise ConversionError, "Failed to parse HTML content" unless doc
17
+ raise ConversionError, 'Failed to parse HTML content' unless doc
18
18
 
19
19
  blocks = []
20
- current_text = ""
20
+ current_text = ''
21
21
 
22
22
  doc.children.each do |node|
23
23
  case node.name
24
- when "h1", "h2", "h3", "h4", "h5", "h6"
24
+ when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
25
25
  # Add any accumulated text as a paragraph before the header
26
26
  if current_text.present?
27
27
  blocks << create_paragraph_block(current_text)
28
- current_text = ""
28
+ current_text = ''
29
29
  end
30
30
 
31
31
  blocks << {
32
- "type" => "header",
33
- "data" => {
34
- "text" => node.text.strip,
35
- "level" => node.name[1].to_i
32
+ 'type' => 'header',
33
+ 'data' => {
34
+ 'text' => node.text.strip,
35
+ 'level' => node.name[1].to_i
36
36
  }
37
37
  }
38
- when "p", "div"
38
+ when 'p', 'div'
39
39
  # Add any accumulated text first
40
40
  if current_text.present?
41
41
  blocks << create_paragraph_block(current_text)
42
- current_text = ""
42
+ current_text = ''
43
43
  end
44
44
 
45
- if node.name == "div"
45
+ if node.name == 'div'
46
46
  # Process div children separately
47
47
  node.children.each do |child|
48
48
  case child.name
49
- when "h1", "h2", "h3", "h4", "h5", "h6"
49
+ when 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
50
50
  blocks << {
51
- "type" => "header",
52
- "data" => {
53
- "text" => child.text.strip,
54
- "level" => child.name[1].to_i
51
+ 'type' => 'header',
52
+ 'data' => {
53
+ 'text' => child.text.strip,
54
+ 'level' => child.name[1].to_i
55
55
  }
56
56
  }
57
- when "p"
57
+ when 'p'
58
58
  text = process_inline_elements(child)
59
59
  paragraphs = text.split(%r{<br\s*/?>\s*<br\s*/?>}).map(&:strip)
60
60
  paragraphs.each do |paragraph|
61
61
  blocks << create_paragraph_block(paragraph) if paragraph.present?
62
62
  end
63
- when "ul", "ol"
64
- items = child.css("li").map { |li| process_inline_elements(li) }
63
+ when 'ul', 'ol'
64
+ items = child.css('li').map { |li| process_inline_elements(li) }
65
65
  next if items.empty?
66
66
 
67
67
  blocks << {
68
- "type" => "list",
69
- "data" => {
70
- "style" => (child.name == "ul") ? "unordered" : "ordered",
71
- "items" => items
68
+ 'type' => 'list',
69
+ 'data' => {
70
+ 'style' => child.name == 'ul' ? 'unordered' : 'ordered',
71
+ 'items' => items
72
72
  }
73
73
  }
74
- when "blockquote"
74
+ when 'blockquote'
75
75
  blocks << {
76
- "type" => "quote",
77
- "data" => {
78
- "text" => process_inline_elements(child),
79
- "caption" => "",
80
- "alignment" => "left"
76
+ 'type' => 'quote',
77
+ 'data' => {
78
+ 'text' => process_inline_elements(child),
79
+ 'caption' => '',
80
+ 'alignment' => 'left'
81
81
  }
82
82
  }
83
- when "text"
83
+ when 'text'
84
84
  text = child.text.strip
85
85
  current_text += text if text.present?
86
86
  end
@@ -93,41 +93,41 @@ module Panda
93
93
  blocks << create_paragraph_block(paragraph) if paragraph.present?
94
94
  end
95
95
  end
96
- when "br"
96
+ when 'br'
97
97
  current_text += "\n\n"
98
- when "text"
98
+ when 'text'
99
99
  text = node.text.strip
100
100
  current_text += text if text.present?
101
- when "ul", "ol"
101
+ when 'ul', 'ol'
102
102
  # Add any accumulated text first
103
103
  if current_text.present?
104
104
  blocks << create_paragraph_block(current_text)
105
- current_text = ""
105
+ current_text = ''
106
106
  end
107
107
 
108
- items = node.css("li").map { |li| process_inline_elements(li) }
108
+ items = node.css('li').map { |li| process_inline_elements(li) }
109
109
  next if items.empty?
110
110
 
111
111
  blocks << {
112
- "type" => "list",
113
- "data" => {
114
- "style" => (node.name == "ul") ? "unordered" : "ordered",
115
- "items" => items
112
+ 'type' => 'list',
113
+ 'data' => {
114
+ 'style' => node.name == 'ul' ? 'unordered' : 'ordered',
115
+ 'items' => items
116
116
  }
117
117
  }
118
- when "blockquote"
118
+ when 'blockquote'
119
119
  # Add any accumulated text first
120
120
  if current_text.present?
121
121
  blocks << create_paragraph_block(current_text)
122
- current_text = ""
122
+ current_text = ''
123
123
  end
124
124
 
125
125
  blocks << {
126
- "type" => "quote",
127
- "data" => {
128
- "text" => process_inline_elements(node),
129
- "caption" => "",
130
- "alignment" => "left"
126
+ 'type' => 'quote',
127
+ 'data' => {
128
+ 'text' => process_inline_elements(node),
129
+ 'caption' => '',
130
+ 'alignment' => 'left'
131
131
  }
132
132
  }
133
133
  end
@@ -138,11 +138,11 @@ module Panda
138
138
 
139
139
  # Return the complete EditorJS structure
140
140
  {
141
- "time" => Time.current.to_i * 1000,
142
- "blocks" => blocks,
143
- "version" => "2.28.2"
141
+ 'time' => Time.current.to_i * 1000,
142
+ 'blocks' => blocks,
143
+ 'version' => '2.28.2'
144
144
  }
145
- rescue => e
145
+ rescue StandardError => e
146
146
  Rails.logger.error "HTML to EditorJS conversion failed: #{e.message}"
147
147
  Rails.logger.error e.backtrace.join("\n")
148
148
  raise ConversionError, "Failed to convert HTML to EditorJS format: #{e.message}"
@@ -151,41 +151,41 @@ module Panda
151
151
 
152
152
  def self.create_paragraph_block(text)
153
153
  {
154
- "type" => "paragraph",
155
- "data" => {
156
- "text" => text.strip
154
+ 'type' => 'paragraph',
155
+ 'data' => {
156
+ 'text' => text.strip
157
157
  }
158
158
  }
159
159
  end
160
160
 
161
161
  def self.process_inline_elements(node)
162
- result = ""
162
+ result = ''
163
163
  node.children.each do |child|
164
164
  case child.name
165
- when "br"
166
- result += "<br>"
167
- when "text"
165
+ when 'br'
166
+ result += '<br>'
167
+ when 'text'
168
168
  result += child.text
169
- when "strong", "b"
169
+ when 'strong', 'b'
170
170
  result += "<b>#{child.text}</b>"
171
- when "em", "i"
171
+ when 'em', 'i'
172
172
  result += "<i>#{child.text}</i>"
173
- when "a"
174
- href = child["href"]
173
+ when 'a'
174
+ href = child['href']
175
175
  text = child.text.strip
176
176
  # Handle email links specially
177
- if href&.start_with?("mailto:")
178
- email = href.sub("mailto:", "")
177
+ if href&.start_with?('mailto:')
178
+ email = href.sub('mailto:', '')
179
179
  result += "<a href=\"mailto:#{email}\">#{text}</a>"
180
180
  else
181
181
  result += "<a href=\"#{href}\">#{text}</a>"
182
182
  end
183
183
  else
184
184
  result += if child.text?
185
- child.text
186
- else
187
- child.to_html
188
- end
185
+ child.text
186
+ else
187
+ child.to_html
188
+ end
189
189
  end
190
190
  end
191
191
  result.strip
data/config/importmap.rb CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  # Pin npm packages by running ./bin/importmap
4
4
 
5
- pin_all_from "app/javascript/panda/editor", under: "panda/editor"
5
+ pin_all_from 'app/javascript/panda/editor', under: 'panda/editor'
6
6
 
7
7
  # EditorJS Core and plugins (from CDN)
8
- pin "@editorjs/editorjs", to: "https://cdn.jsdelivr.net/npm/@editorjs/editorjs@2.28.2/+esm"
9
- pin "@editorjs/paragraph", to: "https://cdn.jsdelivr.net/npm/@editorjs/paragraph@2.11.3/+esm"
10
- pin "@editorjs/header", to: "https://cdn.jsdelivr.net/npm/@editorjs/header@2.8.1/+esm"
11
- pin "@editorjs/nested-list", to: "https://cdn.jsdelivr.net/npm/@editorjs/nested-list@1.4.2/+esm"
12
- pin "@editorjs/quote", to: "https://cdn.jsdelivr.net/npm/@editorjs/quote@2.6.0/+esm"
13
- pin "@editorjs/simple-image", to: "https://cdn.jsdelivr.net/npm/@editorjs/simple-image@1.6.0/+esm"
14
- pin "@editorjs/table", to: "https://cdn.jsdelivr.net/npm/@editorjs/table@2.3.0/+esm"
15
- pin "@editorjs/embed", to: "https://cdn.jsdelivr.net/npm/@editorjs/embed@2.7.0/+esm"
8
+ pin '@editorjs/editorjs', to: 'https://cdn.jsdelivr.net/npm/@editorjs/editorjs@2.28.2/+esm'
9
+ pin '@editorjs/paragraph', to: 'https://cdn.jsdelivr.net/npm/@editorjs/paragraph@2.11.3/+esm'
10
+ pin '@editorjs/header', to: 'https://cdn.jsdelivr.net/npm/@editorjs/header@2.8.1/+esm'
11
+ pin '@editorjs/nested-list', to: 'https://cdn.jsdelivr.net/npm/@editorjs/nested-list@1.4.2/+esm'
12
+ pin '@editorjs/quote', to: 'https://cdn.jsdelivr.net/npm/@editorjs/quote@2.6.0/+esm'
13
+ pin '@editorjs/simple-image', to: 'https://cdn.jsdelivr.net/npm/@editorjs/simple-image@1.6.0/+esm'
14
+ pin '@editorjs/table', to: 'https://cdn.jsdelivr.net/npm/@editorjs/table@2.3.0/+esm'
15
+ pin '@editorjs/embed', to: 'https://cdn.jsdelivr.net/npm/@editorjs/embed@2.7.0/+esm'
data/docs/FOOTNOTES.md CHANGED
@@ -308,9 +308,58 @@ insert at position 11: "Hello world<sup>2</sup>"
308
308
  insert at position 5: "Hello<sup>1</sup> world<sup>2</sup>"
309
309
  ```
310
310
 
311
+ ### Markdown Support
312
+
313
+ The footnote system supports markdown formatting, allowing you to use rich text formatting in your citations.
314
+
315
+ **Enable markdown:**
316
+
317
+ ```ruby
318
+ renderer = Panda::Editor::Renderer.new(content, markdown: true)
319
+ output = renderer.render
320
+ ```
321
+
322
+ **Supported markdown features:**
323
+
324
+ - **Bold text** (`**bold**` or `__bold__`)
325
+ - *Italic text* (`*italic*` or `_italic_`)
326
+ - `Inline code` (`` `code` ``)
327
+ - ~~Strikethrough~~ (`~~text~~`)
328
+ - [Links](url) (`[text](url)`)
329
+ - Automatic URL linking
330
+
331
+ **Example:**
332
+
333
+ ```ruby
334
+ content = {
335
+ "blocks" => [{
336
+ "type" => "paragraph",
337
+ "data" => {
338
+ "text" => "Research findings",
339
+ "footnotes" => [{
340
+ "id" => "fn-1",
341
+ "content" => "Smith, J. (2023). **Important study** on *ADHD treatment*. See https://example.com for details.",
342
+ "position" => 17
343
+ }]
344
+ }
345
+ }]
346
+ }
347
+
348
+ renderer = Panda::Editor::Renderer.new(content, markdown: true)
349
+ # Output will include: Smith, J. (2023). <strong>Important study</strong> on <em>ADHD treatment</em>. See <a href="https://example.com">https://example.com</a> for details.
350
+ ```
351
+
352
+ **Important notes:**
353
+
354
+ - Markdown includes built-in URL autolinking, so you typically don't need `autolink_urls: true` when using markdown
355
+ - However, both options can be used together if needed - the custom autolink_urls will skip URLs that markdown already linked
356
+ - Markdown links are rendered with `target="_blank"` and `rel="noopener noreferrer"` for security
357
+ - Images are disabled in markdown footnotes for security
358
+ - HTML styles are stripped from markdown output
359
+
311
360
  ### Auto-linking URLs
312
361
 
313
- The footnote system can automatically convert plain URLs in footnote content into clickable links.
362
+ The footnote system can automatically convert plain URLs in footnote content into clickable links when markdown is not enabled.
314
363
 
315
364
  **Enable auto-linking:**
316
365
 
@@ -575,7 +624,7 @@ bundle exec rspec spec/lib/panda/editor/renderer_spec.rb
575
624
  Potential improvements for future versions:
576
625
 
577
626
  - [ ] Support for footnotes in other block types (headers, quotes, etc.)
578
- - [ ] Rich text formatting within footnote content
627
+ - [x] Rich text formatting within footnote content (implemented via markdown support)
579
628
  - [ ] Footnote tooltips on hover
580
629
  - [ ] Customizable footnote markers (*, †, ‡, etc.)
581
630
  - [ ] Export footnotes to bibliography formats (BibTeX, etc.)
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "net/http"
4
- require "json"
3
+ require 'net/http'
4
+ require 'json'
5
5
 
6
6
  module Panda
7
7
  module Editor
8
8
  class AssetLoader
9
- GITHUB_RELEASES_URL = "https://api.github.com/repos/tastybamboo/panda-editor/releases/latest"
10
- ASSET_CACHE_DIR = Rails.root.join("tmp", "panda_editor_assets")
9
+ GITHUB_RELEASES_URL = 'https://api.github.com/repos/tastybamboo/panda-editor/releases/latest'
10
+ ASSET_CACHE_DIR = Rails.root.join('tmp', 'panda_editor_assets')
11
11
 
12
12
  class << self
13
13
  def load_assets
@@ -22,7 +22,7 @@ module Panda
22
22
  if use_compiled_assets?
23
23
  compiled_javascript_url
24
24
  else
25
- "/assets/panda/editor/application.js"
25
+ '/assets/panda/editor/application.js'
26
26
  end
27
27
  end
28
28
 
@@ -30,7 +30,7 @@ module Panda
30
30
  if use_compiled_assets?
31
31
  compiled_stylesheet_url
32
32
  else
33
- "/assets/panda/editor/application.css"
33
+ '/assets/panda/editor/application.css'
34
34
  end
35
35
  end
36
36
 
@@ -39,7 +39,7 @@ module Panda
39
39
  def use_compiled_assets?
40
40
  Rails.env.production? ||
41
41
  Rails.env.test? ||
42
- ENV["PANDA_EDITOR_USE_COMPILED_ASSETS"] == "true"
42
+ ENV['PANDA_EDITOR_USE_COMPILED_ASSETS'] == 'true'
43
43
  end
44
44
 
45
45
  def load_compiled_assets
@@ -52,23 +52,23 @@ module Panda
52
52
 
53
53
  def load_development_assets
54
54
  {
55
- javascript: "/assets/panda/editor/application.js",
56
- stylesheet: "/assets/panda/editor/application.css"
55
+ javascript: '/assets/panda/editor/application.js',
56
+ stylesheet: '/assets/panda/editor/application.css'
57
57
  }
58
58
  end
59
59
 
60
60
  def compiled_javascript_url
61
- asset_path = find_latest_asset("js")
61
+ asset_path = find_latest_asset('js')
62
62
  asset_path ? "/panda-editor-assets/#{File.basename(asset_path)}" : nil
63
63
  end
64
64
 
65
65
  def compiled_stylesheet_url
66
- asset_path = find_latest_asset("css")
66
+ asset_path = find_latest_asset('css')
67
67
  asset_path ? "/panda-editor-assets/#{File.basename(asset_path)}" : nil
68
68
  end
69
69
 
70
70
  def find_latest_asset(extension)
71
- pattern = Rails.root.join("public", "panda-editor-assets", "panda-editor-*.#{extension}")
71
+ pattern = Rails.root.join('public', 'panda-editor-assets', "panda-editor-*.#{extension}")
72
72
  Dir.glob(pattern).max_by { |f| File.mtime(f) }
73
73
  end
74
74
 
@@ -79,18 +79,18 @@ module Panda
79
79
  end
80
80
 
81
81
  def assets_exist?
82
- js_exists = Dir.glob(Rails.root.join("public", "panda-editor-assets", "panda-editor-*.js")).any?
83
- css_exists = Dir.glob(Rails.root.join("public", "panda-editor-assets", "panda-editor-*.css")).any?
82
+ js_exists = Dir.glob(Rails.root.join('public', 'panda-editor-assets', 'panda-editor-*.js')).any?
83
+ css_exists = Dir.glob(Rails.root.join('public', 'panda-editor-assets', 'panda-editor-*.css')).any?
84
84
  js_exists && css_exists
85
85
  end
86
86
 
87
87
  def download_assets_from_github
88
- Rails.logger.info "[Panda Editor] Downloading assets from GitHub releases..."
88
+ Rails.logger.info '[Panda Editor] Downloading assets from GitHub releases...'
89
89
 
90
90
  begin
91
91
  release_data = fetch_latest_release
92
- download_release_assets(release_data["assets"])
93
- rescue => e
92
+ download_release_assets(release_data['assets'])
93
+ rescue StandardError => e
94
94
  Rails.logger.error "[Panda Editor] Failed to download assets: #{e.message}"
95
95
  use_fallback_assets
96
96
  end
@@ -100,32 +100,30 @@ module Panda
100
100
  uri = URI(GITHUB_RELEASES_URL)
101
101
  response = Net::HTTP.get_response(uri)
102
102
 
103
- if response.code == "200"
104
- JSON.parse(response.body)
105
- else
106
- raise "GitHub API returned #{response.code}"
107
- end
103
+ raise "GitHub API returned #{response.code}" unless response.code == '200'
104
+
105
+ JSON.parse(response.body)
108
106
  end
109
107
 
110
108
  def download_release_assets(assets)
111
109
  assets.each do |asset|
112
- next unless asset["name"].match?(/panda-editor.*\.(js|css)$/)
110
+ next unless asset['name'].match?(/panda-editor.*\.(js|css)$/)
113
111
 
114
112
  download_asset(asset)
115
113
  end
116
114
  end
117
115
 
118
116
  def download_asset(asset)
119
- uri = URI(asset["browser_download_url"])
117
+ uri = URI(asset['browser_download_url'])
120
118
  response = Net::HTTP.get_response(uri)
121
119
 
122
- if response.code == "200"
123
- save_asset(asset["name"], response.body)
124
- end
120
+ return unless response.code == '200'
121
+
122
+ save_asset(asset['name'], response.body)
125
123
  end
126
124
 
127
125
  def save_asset(filename, content)
128
- dir = Rails.root.join("public", "panda-editor-assets")
126
+ dir = Rails.root.join('public', 'panda-editor-assets')
129
127
  FileUtils.mkdir_p(dir)
130
128
 
131
129
  File.write(dir.join(filename), content)
@@ -133,17 +131,17 @@ module Panda
133
131
  end
134
132
 
135
133
  def use_fallback_assets
136
- Rails.logger.warn "[Panda Editor] Using fallback embedded assets"
134
+ Rails.logger.warn '[Panda Editor] Using fallback embedded assets'
137
135
  # Copy embedded assets from gem to public directory
138
136
  copy_embedded_assets
139
137
  end
140
138
 
141
139
  def copy_embedded_assets
142
- source_dir = Panda::Editor::Engine.root.join("public", "panda-editor-assets")
143
- dest_dir = Rails.root.join("public", "panda-editor-assets")
140
+ source_dir = Panda::Editor::Engine.root.join('public', 'panda-editor-assets')
141
+ dest_dir = Rails.root.join('public', 'panda-editor-assets')
144
142
 
145
143
  FileUtils.mkdir_p(dest_dir)
146
- FileUtils.cp_r(Dir.glob(source_dir.join("*")), dest_dir)
144
+ FileUtils.cp_r(Dir.glob(source_dir.join('*')), dest_dir)
147
145
  end
148
146
  end
149
147
  end
@@ -5,13 +5,13 @@ module Panda
5
5
  module Blocks
6
6
  class Alert < Base
7
7
  def render
8
- message = sanitize(data["message"])
9
- type = data["type"] || "primary"
8
+ message = sanitize(data['message'])
9
+ type = data['type'] || 'primary'
10
10
 
11
11
  html_safe(
12
12
  "<div class=\"#{alert_classes(type)} p-4 mb-4 rounded-lg\">" \
13
13
  "#{message}" \
14
- "</div>"
14
+ '</div>'
15
15
  )
16
16
  end
17
17
 
@@ -19,13 +19,13 @@ module Panda
19
19
 
20
20
  def alert_classes(type)
21
21
  case type
22
- when "primary" then "bg-blue-100 text-blue-800"
23
- when "secondary" then "bg-gray-100 text-gray-800"
24
- when "success" then "bg-green-100 text-green-800"
25
- when "danger" then "bg-red-100 text-red-800"
26
- when "warning" then "bg-yellow-100 text-yellow-800"
27
- when "info" then "bg-indigo-100 text-indigo-800"
28
- else "bg-blue-100 text-blue-800"
22
+ when 'primary' then 'bg-blue-100 text-blue-800'
23
+ when 'secondary' then 'bg-gray-100 text-gray-800'
24
+ when 'success' then 'bg-green-100 text-green-800'
25
+ when 'danger' then 'bg-red-100 text-red-800'
26
+ when 'warning' then 'bg-yellow-100 text-yellow-800'
27
+ when 'info' then 'bg-indigo-100 text-indigo-800'
28
+ else 'bg-blue-100 text-blue-800'
29
29
  end
30
30
  end
31
31
  end
@@ -15,7 +15,7 @@ module Panda
15
15
  end
16
16
 
17
17
  def render
18
- ""
18
+ ''
19
19
  end
20
20
 
21
21
  protected
@@ -5,8 +5,8 @@ module Panda
5
5
  module Blocks
6
6
  class Header < Base
7
7
  def render
8
- content = sanitize(data["text"])
9
- level = data["level"] || 2
8
+ content = sanitize(data['text'])
9
+ level = data['level'] || 2
10
10
  html_safe("<h#{level}>#{content}</h#{level}>")
11
11
  end
12
12
  end