inkpen 0.9.2 → 0.9.4

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: 92089e260ac3068e3e269e864d4ec9f0294447bbb4e28764f02f5c722debc155
4
- data.tar.gz: 1b824dd56c5ccd3f1c448cf841ea53db2660617f14a6b4fe5a41ee9b20f15f7e
3
+ metadata.gz: a928ec9dfc4d7e10782b6b51c17ee58487f13057ef6b5efa1bdb2c39858f292a
4
+ data.tar.gz: d9bd76686b1dfe78f494c7d79d5d723f708b964a568439334199dd18f0424f41
5
5
  SHA512:
6
- metadata.gz: 5d6ceb94d0867ab0b0a5194f6a1468454c1ef8b6782b94cd62ce469a15667ca04ad3c764681130c1e148cc823ef0b8cdd770a0822a01ffb402be632300869ce0
7
- data.tar.gz: 9420e9694ce248bec30176e60e26a3521749a1e7b5463ba14423a58b87df6b18b40a82b82ff5ab9aa1f9146637f8330e32f1abb309744ca89d09346deaa28e89
6
+ metadata.gz: 0e5254caa121d60d922fec39e797780f6d4e3eeee2ed979f1debc3a58e436df063a5818b04e4c050e6600cd6de60f7fcf30fad83b9585e7e437b3c86a45161bb
7
+ data.tar.gz: 3d0be135fbc285b521ca490113fb988390ee8a2ca9349c8d3ebe7140ca5f9647adf94c27716b1a124103b0838ce8559fbe43249e336a5b0901413569c55b0cab
data/.rubocop.yml CHANGED
@@ -1,5 +1,23 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 3.1
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ # Releases use the repository-scoped RUBYGEMS_API_KEY managed by GitHub Actions.
7
+ Gemspec/RequireMFA:
8
+ Enabled: false
9
+
10
+ Metrics/ClassLength:
11
+ Exclude:
12
+ - "test/**/*"
13
+
14
+ Metrics/AbcSize:
15
+ Exclude:
16
+ - "test/**/*"
17
+
18
+ Metrics/MethodLength:
19
+ Exclude:
20
+ - "test/**/*"
3
21
 
4
22
  Style/StringLiterals:
5
23
  EnforcedStyle: double_quotes
@@ -47,18 +47,9 @@ module Inkpen
47
47
  # <%= inkpen_field :post, :body, extensions: [:mentions, :hashtags] %>
48
48
  #
49
49
  def inkpen_field(object_name, method, options = {})
50
- value = options.delete(:value)
51
-
52
- # Try to get value from object if not provided
53
- if value.nil? && (object = instance_variable_get("@#{object_name}"))
54
- value = object.public_send(method) if object.respond_to?(method)
55
- end
56
-
57
- name = "#{object_name}[#{method}]"
58
-
59
50
  editor = Inkpen::Editor.new(
60
- name: name,
61
- value: value,
51
+ name: "#{object_name}[#{method}]",
52
+ value: inkpen_field_value(object_name, method, options.delete(:value)),
62
53
  **options
63
54
  )
64
55
 
@@ -110,5 +101,12 @@ module Inkpen
110
101
  def render_inkpen_editor(editor)
111
102
  render partial: "inkpen/editor", locals: { editor: editor }
112
103
  end
104
+
105
+ def inkpen_field_value(object_name, method, explicit_value)
106
+ return explicit_value unless explicit_value.nil?
107
+
108
+ object = instance_variable_get("@#{object_name}")
109
+ object.public_send(method) if object.respond_to?(method)
110
+ end
113
111
  end
114
112
  end
data/config/importmap.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Inkpen importmap — single vendored bundle, no third-party CDN.
2
4
  #
3
5
  # Before 0.8.0 this file pinned ~70 modules to a third-party module CDN.
data/docs/CHANGELOG.md CHANGED
@@ -11,6 +11,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ---
13
13
 
14
+ ## [0.9.4] - 2026-10-09
15
+
16
+ ### Changed
17
+ - Removed the gem-level MFA publishing requirement so the existing repository-scoped RubyGems API key can publish releases from CI.
18
+ - Restored the established API-key release workflow used by the other maintained gems; runtime editor behavior is unchanged.
19
+
20
+ ---
21
+
22
+ ## [0.9.3] - 2026-10-09
23
+
24
+ ### Fixed
25
+ - Restored a green Ruby CI baseline by explicitly enabling new RuboCop cops and resolving the existing offenses without changing the editor's public behavior.
26
+ - Added characterization coverage for editor initialization, data attributes, and model-bound field values so future refactors cannot silently change the Rails integration contract.
27
+
28
+ ---
29
+
14
30
  ## [0.9.2] - 2026-07-11
15
31
 
16
32
  ### Fixed
data/lib/inkpen/editor.rb CHANGED
@@ -102,17 +102,9 @@ module Inkpen
102
102
  def initialize(name:, value: nil, **options)
103
103
  @name = name
104
104
  @value = value
105
- @toolbar = options.fetch(:toolbar, Inkpen.configuration.toolbar)
106
- @sticky_toolbar = options.fetch(:sticky_toolbar, nil)
107
- @markdown_mode = options.fetch(:markdown_mode, nil)
108
- @extensions = options.fetch(:extensions, Inkpen.configuration.extensions)
109
- @extension_config = options.fetch(:extension_config, {})
110
- @placeholder = options.fetch(:placeholder, Inkpen.configuration.placeholder)
111
- @autosave = options.fetch(:autosave, Inkpen.configuration.autosave)
112
- @autosave_interval = options.fetch(:autosave_interval, Inkpen.configuration.autosave_interval)
113
- @min_height = options.fetch(:min_height, Inkpen.configuration.min_height)
114
- @max_height = options.fetch(:max_height, Inkpen.configuration.max_height)
115
- @html_attributes = options.fetch(:html, {})
105
+ assign_interface_options(options)
106
+ assign_behavior_options(options)
107
+ assign_dimension_options(options)
116
108
  end
117
109
 
118
110
  ##
@@ -124,32 +116,7 @@ module Inkpen
124
116
  # @return [Hash] data attributes hash
125
117
  #
126
118
  def data_attributes
127
- controllers = ["inkpen--editor"]
128
- controllers << "inkpen--sticky-toolbar" if sticky_toolbar&.enabled?
129
-
130
- attrs = {
131
- data: {
132
- controller: controllers.join(" "),
133
- "inkpen--editor-extensions-value" => extensions.to_json,
134
- "inkpen--editor-extension-config-value" => extension_config.to_json,
135
- "inkpen--editor-toolbar-value" => toolbar.to_s,
136
- "inkpen--editor-placeholder-value" => placeholder,
137
- "inkpen--editor-autosave-value" => autosave.to_s,
138
- "inkpen--editor-autosave-interval-value" => autosave_interval.to_s
139
- }
140
- }
141
-
142
- # Merge sticky toolbar data attributes if enabled
143
- if sticky_toolbar&.enabled?
144
- attrs[:data].merge!(sticky_toolbar.data_attributes)
145
- end
146
-
147
- # Merge markdown mode data attributes if enabled
148
- if markdown_mode&.enabled?
149
- attrs[:data].merge!(markdown_mode.data_attributes)
150
- end
151
-
152
- attrs
119
+ { data: editor_data_attributes.merge(optional_data_attributes) }
153
120
  end
154
121
 
155
122
  ##
@@ -200,5 +167,53 @@ module Inkpen
200
167
  def input_id
201
168
  name.to_s.gsub(/[\[\]]/, "_").gsub(/_+/, "_").chomp("_")
202
169
  end
170
+
171
+ private
172
+
173
+ def assign_interface_options(options)
174
+ @toolbar = options.fetch(:toolbar, Inkpen.configuration.toolbar)
175
+ @sticky_toolbar = options.fetch(:sticky_toolbar, nil)
176
+ @markdown_mode = options.fetch(:markdown_mode, nil)
177
+ @extensions = options.fetch(:extensions, Inkpen.configuration.extensions)
178
+ @extension_config = options.fetch(:extension_config, {})
179
+ @placeholder = options.fetch(:placeholder, Inkpen.configuration.placeholder)
180
+ end
181
+
182
+ def assign_behavior_options(options)
183
+ @autosave = options.fetch(:autosave, Inkpen.configuration.autosave)
184
+ @autosave_interval = options.fetch(:autosave_interval, Inkpen.configuration.autosave_interval)
185
+ end
186
+
187
+ def assign_dimension_options(options)
188
+ @min_height = options.fetch(:min_height, Inkpen.configuration.min_height)
189
+ @max_height = options.fetch(:max_height, Inkpen.configuration.max_height)
190
+ @html_attributes = options.fetch(:html, {})
191
+ end
192
+
193
+ def editor_data_attributes
194
+ {
195
+ controller: editor_controllers.join(" "),
196
+ "inkpen--editor-extensions-value" => extensions.to_json,
197
+ "inkpen--editor-extension-config-value" => extension_config.to_json,
198
+ "inkpen--editor-toolbar-value" => toolbar.to_s,
199
+ "inkpen--editor-placeholder-value" => placeholder,
200
+ "inkpen--editor-autosave-value" => autosave.to_s,
201
+ "inkpen--editor-autosave-interval-value" => autosave_interval.to_s
202
+ }
203
+ end
204
+
205
+ def editor_controllers
206
+ controllers = ["inkpen--editor"]
207
+ controllers << "inkpen--sticky-toolbar" if sticky_toolbar&.enabled?
208
+ controllers
209
+ end
210
+
211
+ def optional_data_attributes
212
+ attributes = {}
213
+ attributes.merge!(sticky_toolbar.data_attributes) if sticky_toolbar&.enabled?
214
+ attributes.merge!(markdown_mode.data_attributes) if markdown_mode&.enabled?
215
+
216
+ attributes
217
+ end
203
218
  end
204
219
  end
data/lib/inkpen/engine.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inkpen
4
+ # Rails integration for Inkpen assets, import maps, and view helpers.
4
5
  class Engine < ::Rails::Engine
5
6
  isolate_namespace Inkpen
6
7
 
@@ -147,6 +147,8 @@ module Inkpen
147
147
  #
148
148
  # @return [Hash] configuration for the TipTap extension
149
149
  #
150
+ # Declarative JavaScript extension configuration is clearest as one hash.
151
+ # rubocop:disable Metrics/MethodLength
150
152
  def to_config
151
153
  {
152
154
  languages: languages,
@@ -160,6 +162,7 @@ module Inkpen
160
162
  }
161
163
  }
162
164
  end
165
+ # rubocop:enable Metrics/MethodLength
163
166
 
164
167
  private
165
168
 
@@ -70,7 +70,7 @@ module Inkpen
70
70
  #
71
71
  def max_depth
72
72
  depth = options.fetch(:max_depth, DEFAULT_MAX_DEPTH)
73
- [[depth, 1].max, 3].min # Clamp between 1 and 3
73
+ depth.clamp(1, 3)
74
74
  end
75
75
 
76
76
  ##
@@ -142,6 +142,8 @@ module Inkpen
142
142
  #
143
143
  # @return [Hash] configuration for the TipTap extension
144
144
  #
145
+ # Declarative JavaScript extension configuration is clearest as one hash.
146
+ # rubocop:disable Metrics/MethodLength
145
147
  def to_config
146
148
  config = {
147
149
  titleLevel: title_level,
@@ -164,6 +166,7 @@ module Inkpen
164
166
 
165
167
  config
166
168
  end
169
+ # rubocop:enable Metrics/MethodLength
167
170
 
168
171
  private
169
172
 
@@ -28,30 +28,46 @@ module Inkpen
28
28
  # Default command set matching Notion/Paper experience
29
29
  DEFAULT_COMMANDS = [
30
30
  # Text blocks
31
- { name: "paragraph", label: "Text", description: "Plain text block", icon: "text", group: "Basic", shortcut: nil },
32
- { name: "heading1", label: "Heading 1", description: "Large heading", icon: "h1", group: "Basic", shortcut: "#" },
33
- { name: "heading2", label: "Heading 2", description: "Medium heading", icon: "h2", group: "Basic", shortcut: "##" },
34
- { name: "heading3", label: "Heading 3", description: "Small heading", icon: "h3", group: "Basic", shortcut: "###" },
31
+ { name: "paragraph", label: "Text", description: "Plain text block", icon: "text", group: "Basic",
32
+ shortcut: nil },
33
+ { name: "heading1", label: "Heading 1", description: "Large heading", icon: "h1", group: "Basic",
34
+ shortcut: "#" },
35
+ { name: "heading2", label: "Heading 2", description: "Medium heading", icon: "h2", group: "Basic",
36
+ shortcut: "##" },
37
+ { name: "heading3", label: "Heading 3", description: "Small heading", icon: "h3", group: "Basic",
38
+ shortcut: "###" },
35
39
 
36
40
  # Lists
37
- { name: "bullet_list", label: "Bullet List", description: "Unordered list", icon: "list", group: "Lists", shortcut: "-" },
38
- { name: "ordered_list", label: "Numbered List", description: "Ordered list", icon: "list-ordered", group: "Lists", shortcut: "1." },
39
- { name: "task_list", label: "Task List", description: "Checklist with checkboxes", icon: "check-square", group: "Lists", shortcut: "[]" },
41
+ { name: "bullet_list", label: "Bullet List", description: "Unordered list", icon: "list", group: "Lists",
42
+ shortcut: "-" },
43
+ { name: "ordered_list", label: "Numbered List", description: "Ordered list", icon: "list-ordered",
44
+ group: "Lists", shortcut: "1." },
45
+ { name: "task_list", label: "Task List", description: "Checklist with checkboxes", icon: "check-square",
46
+ group: "Lists", shortcut: "[]" },
40
47
 
41
48
  # Blocks
42
- { name: "blockquote", label: "Quote", description: "Quote block", icon: "quote", group: "Blocks", shortcut: ">" },
43
- { name: "code_block", label: "Code Block", description: "Code with syntax highlighting", icon: "code", group: "Blocks", shortcut: "```" },
44
- { name: "horizontal_rule", label: "Divider", description: "Horizontal line", icon: "minus", group: "Blocks", shortcut: "---" },
49
+ { name: "blockquote", label: "Quote", description: "Quote block", icon: "quote", group: "Blocks",
50
+ shortcut: ">" },
51
+ { name: "code_block", label: "Code Block", description: "Code with syntax highlighting", icon: "code",
52
+ group: "Blocks", shortcut: "```" },
53
+ { name: "horizontal_rule", label: "Divider", description: "Horizontal line", icon: "minus", group: "Blocks",
54
+ shortcut: "---" },
45
55
 
46
56
  # Media
47
- { name: "image", label: "Image", description: "Upload or embed an image", icon: "image", group: "Media", shortcut: nil },
48
- { name: "youtube", label: "YouTube", description: "Embed a YouTube video", icon: "youtube", group: "Media", shortcut: nil },
57
+ { name: "image", label: "Image", description: "Upload or embed an image", icon: "image", group: "Media",
58
+ shortcut: nil },
59
+ { name: "youtube", label: "YouTube", description: "Embed a YouTube video", icon: "youtube", group: "Media",
60
+ shortcut: nil },
49
61
 
50
62
  # Advanced
51
- { name: "table", label: "Table", description: "Insert a table", icon: "table", group: "Advanced", shortcut: nil },
52
- { name: "callout", label: "Callout", description: "Highlighted callout box", icon: "alert-circle", group: "Advanced", shortcut: nil },
53
- { name: "section", label: "Section", description: "Page section with width control", icon: "layout", group: "Advanced", shortcut: nil },
54
- { name: "preformatted", label: "Plain Text", description: "Preformatted text for ASCII art", icon: "file-text", group: "Advanced", shortcut: nil }
63
+ { name: "table", label: "Table", description: "Insert a table", icon: "table", group: "Advanced",
64
+ shortcut: nil },
65
+ { name: "callout", label: "Callout", description: "Highlighted callout box", icon: "alert-circle",
66
+ group: "Advanced", shortcut: nil },
67
+ { name: "section", label: "Section", description: "Page section with width control", icon: "layout",
68
+ group: "Advanced", shortcut: nil },
69
+ { name: "preformatted", label: "Plain Text", description: "Preformatted text for ASCII art", icon: "file-text",
70
+ group: "Advanced", shortcut: nil }
55
71
  ].freeze
56
72
 
57
73
  DEFAULT_GROUPS = %w[Basic Lists Blocks Media Advanced].freeze
@@ -60,6 +76,8 @@ module Inkpen
60
76
  :slash_commands
61
77
  end
62
78
 
79
+ # Declarative JavaScript extension configuration is clearest as one hash.
80
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
63
81
  def to_config
64
82
  {
65
83
  trigger: options[:trigger],
@@ -76,9 +94,12 @@ module Inkpen
76
94
  groupClass: options[:group_class]
77
95
  }.compact
78
96
  end
97
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
79
98
 
80
99
  private
81
100
 
101
+ # Option defaults mirror the JavaScript extension's public configuration.
102
+ # rubocop:disable Metrics/MethodLength
82
103
  def default_options
83
104
  super.merge(
84
105
  trigger: "/",
@@ -95,6 +116,7 @@ module Inkpen
95
116
  group_class: "inkpen-slash-group"
96
117
  )
97
118
  end
119
+ # rubocop:enable Metrics/MethodLength
98
120
  end
99
121
  end
100
122
  end
@@ -145,6 +145,8 @@ module Inkpen
145
145
  #
146
146
  # @return [Hash] configuration for the TipTap extension
147
147
  #
148
+ # Declarative JavaScript extension configuration is clearest as one hash.
149
+ # rubocop:disable Metrics/MethodLength
148
150
  def to_config
149
151
  {
150
152
  resizable: resizable?,
@@ -160,9 +162,12 @@ module Inkpen
160
162
  withHeaderRow: with_header_row?
161
163
  }.compact
162
164
  end
165
+ # rubocop:enable Metrics/MethodLength
163
166
 
164
167
  private
165
168
 
169
+ # Option defaults mirror the JavaScript extension's public configuration.
170
+ # rubocop:disable Metrics/MethodLength
166
171
  def default_options
167
172
  super.merge(
168
173
  resizable: true,
@@ -177,6 +182,7 @@ module Inkpen
177
182
  with_header_row: true
178
183
  )
179
184
  end
185
+ # rubocop:enable Metrics/MethodLength
180
186
  end
181
187
  end
182
188
  end
@@ -117,6 +117,8 @@ module Inkpen
117
117
  #
118
118
  # @return [Hash] configuration for the TipTap extension
119
119
  #
120
+ # Declarative JavaScript extension configuration is clearest as one hash.
121
+ # rubocop:disable Metrics/MethodLength
120
122
  def to_config
121
123
  {
122
124
  nested: nested?,
@@ -130,6 +132,7 @@ module Inkpen
130
132
  keyboardShortcut: keyboard_shortcut
131
133
  }
132
134
  end
135
+ # rubocop:enable Metrics/MethodLength
133
136
 
134
137
  private
135
138
 
@@ -80,6 +80,8 @@ module Inkpen
80
80
  # @param sync_delay [Integer] debounce delay for split sync (ms)
81
81
  # @param keyboard_shortcuts [Boolean] enable keyboard shortcuts
82
82
  #
83
+ # Seven keywords form the stable public configuration API.
84
+ # rubocop:disable Metrics/ParameterLists
83
85
  def initialize(
84
86
  enabled: false,
85
87
  default_mode: :wysiwyg,
@@ -97,6 +99,7 @@ module Inkpen
97
99
  @sync_delay = sync_delay.to_i
98
100
  @keyboard_shortcuts = keyboard_shortcuts
99
101
  end
102
+ # rubocop:enable Metrics/ParameterLists
100
103
 
101
104
  ##
102
105
  # Check if markdown mode is enabled.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Inkpen
4
- VERSION = "0.9.2"
4
+ VERSION = "0.9.4"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inkpen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nauman Tariq
@@ -10,33 +10,33 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: rails
13
+ name: importmap-rails
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: '7.0'
18
+ version: '1.0'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '7.0'
25
+ version: '1.0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: importmap-rails
27
+ name: rails
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '1.0'
32
+ version: '7.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '1.0'
39
+ version: '7.0'
40
40
  description: Inkpen provides a modern, extensible rich text editor built on TipTap/ProseMirror
41
41
  with Stimulus controllers for Rails applications.
42
42
  email: