inkpen 0.7.1

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 (95) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.rubocop.yml +8 -0
  4. data/.yardopts +11 -0
  5. data/CLAUDE.md +141 -0
  6. data/README.md +409 -0
  7. data/Rakefile +19 -0
  8. data/app/assets/javascripts/inkpen/controllers/editor_controller.js +2050 -0
  9. data/app/assets/javascripts/inkpen/controllers/sticky_toolbar_controller.js +667 -0
  10. data/app/assets/javascripts/inkpen/controllers/toolbar_controller.js +693 -0
  11. data/app/assets/javascripts/inkpen/export/html.js +637 -0
  12. data/app/assets/javascripts/inkpen/export/index.js +30 -0
  13. data/app/assets/javascripts/inkpen/export/markdown.js +697 -0
  14. data/app/assets/javascripts/inkpen/export/pdf.js +372 -0
  15. data/app/assets/javascripts/inkpen/extensions/advanced_table.js +640 -0
  16. data/app/assets/javascripts/inkpen/extensions/block_commands.js +300 -0
  17. data/app/assets/javascripts/inkpen/extensions/block_gutter.js +338 -0
  18. data/app/assets/javascripts/inkpen/extensions/callout.js +303 -0
  19. data/app/assets/javascripts/inkpen/extensions/columns.js +403 -0
  20. data/app/assets/javascripts/inkpen/extensions/database.js +990 -0
  21. data/app/assets/javascripts/inkpen/extensions/document_section.js +352 -0
  22. data/app/assets/javascripts/inkpen/extensions/drag_handle.js +407 -0
  23. data/app/assets/javascripts/inkpen/extensions/embed.js +629 -0
  24. data/app/assets/javascripts/inkpen/extensions/enhanced_image.js +566 -0
  25. data/app/assets/javascripts/inkpen/extensions/export_commands.js +271 -0
  26. data/app/assets/javascripts/inkpen/extensions/file_attachment.js +593 -0
  27. data/app/assets/javascripts/inkpen/extensions/inkpen_table/index.js +58 -0
  28. data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table.js +638 -0
  29. data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table_cell.js +100 -0
  30. data/app/assets/javascripts/inkpen/extensions/inkpen_table/inkpen_table_header.js +100 -0
  31. data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_constants.js +152 -0
  32. data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_helpers.js +254 -0
  33. data/app/assets/javascripts/inkpen/extensions/inkpen_table/table_menu.js +282 -0
  34. data/app/assets/javascripts/inkpen/extensions/preformatted.js +239 -0
  35. data/app/assets/javascripts/inkpen/extensions/section.js +281 -0
  36. data/app/assets/javascripts/inkpen/extensions/section_title.js +126 -0
  37. data/app/assets/javascripts/inkpen/extensions/slash_commands.js +439 -0
  38. data/app/assets/javascripts/inkpen/extensions/table_of_contents.js +474 -0
  39. data/app/assets/javascripts/inkpen/extensions/toggle_block.js +332 -0
  40. data/app/assets/javascripts/inkpen/index.js +87 -0
  41. data/app/assets/stylesheets/inkpen/advanced_table.css +514 -0
  42. data/app/assets/stylesheets/inkpen/animations.css +626 -0
  43. data/app/assets/stylesheets/inkpen/block_gutter.css +265 -0
  44. data/app/assets/stylesheets/inkpen/callout.css +359 -0
  45. data/app/assets/stylesheets/inkpen/columns.css +314 -0
  46. data/app/assets/stylesheets/inkpen/database.css +658 -0
  47. data/app/assets/stylesheets/inkpen/document_section.css +305 -0
  48. data/app/assets/stylesheets/inkpen/drag_drop.css +220 -0
  49. data/app/assets/stylesheets/inkpen/editor.css +652 -0
  50. data/app/assets/stylesheets/inkpen/embed.css +468 -0
  51. data/app/assets/stylesheets/inkpen/enhanced_image.css +453 -0
  52. data/app/assets/stylesheets/inkpen/export.css +499 -0
  53. data/app/assets/stylesheets/inkpen/file_attachment.css +347 -0
  54. data/app/assets/stylesheets/inkpen/footnotes.css +136 -0
  55. data/app/assets/stylesheets/inkpen/inkpen_table.css +608 -0
  56. data/app/assets/stylesheets/inkpen/preformatted.css +215 -0
  57. data/app/assets/stylesheets/inkpen/search_replace.css +58 -0
  58. data/app/assets/stylesheets/inkpen/section.css +236 -0
  59. data/app/assets/stylesheets/inkpen/slash_menu.css +252 -0
  60. data/app/assets/stylesheets/inkpen/sticky_toolbar.css +314 -0
  61. data/app/assets/stylesheets/inkpen/toc.css +386 -0
  62. data/app/assets/stylesheets/inkpen/toggle.css +260 -0
  63. data/app/helpers/inkpen/editor_helper.rb +114 -0
  64. data/app/views/inkpen/_editor.html.erb +139 -0
  65. data/config/importmap.rb +170 -0
  66. data/docs/.DS_Store +0 -0
  67. data/docs/CHANGELOG.md +571 -0
  68. data/docs/FEATURES.md +436 -0
  69. data/docs/ROADMAP.md +3029 -0
  70. data/docs/VISION.md +235 -0
  71. data/docs/extensions/INKPEN_TABLE.md +482 -0
  72. data/docs/thinking/CORRECTED_NO_VUE.md +756 -0
  73. data/docs/thinking/EXECUTIVE_SUMMARY.md +403 -0
  74. data/docs/thinking/INKPEN_CODE_SAMPLES.md +1479 -0
  75. data/docs/thinking/INKPEN_MASTER_GUIDE.md +891 -0
  76. data/docs/thinking/README_START_HERE.md +341 -0
  77. data/lib/inkpen/configuration.rb +175 -0
  78. data/lib/inkpen/editor.rb +204 -0
  79. data/lib/inkpen/engine.rb +32 -0
  80. data/lib/inkpen/extensions/base.rb +109 -0
  81. data/lib/inkpen/extensions/code_block_syntax.rb +177 -0
  82. data/lib/inkpen/extensions/document_section.rb +111 -0
  83. data/lib/inkpen/extensions/forced_document.rb +183 -0
  84. data/lib/inkpen/extensions/mention.rb +155 -0
  85. data/lib/inkpen/extensions/preformatted.rb +111 -0
  86. data/lib/inkpen/extensions/section.rb +139 -0
  87. data/lib/inkpen/extensions/slash_commands.rb +100 -0
  88. data/lib/inkpen/extensions/table.rb +182 -0
  89. data/lib/inkpen/extensions/task_list.rb +145 -0
  90. data/lib/inkpen/sticky_toolbar.rb +157 -0
  91. data/lib/inkpen/toolbar.rb +145 -0
  92. data/lib/inkpen/version.rb +5 -0
  93. data/lib/inkpen.rb +101 -0
  94. data/sig/inkpen.rbs +4 -0
  95. metadata +165 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 066e1936d8ce5802174354accd02c7e00a958bde47fc5e207463691afb1bc360
4
+ data.tar.gz: a25e8197e62b9227b61e8a7ab9f8ca682372aa7fd08d7a9b15c5d199457d1c15
5
+ SHA512:
6
+ metadata.gz: e57e7c1a93053612ac75e96d1265a6907e531a1061b5b71be173cf1320ffe5346d0966b4b7099ac6dc0f0b4858c8503894bc68d729bf58d38c4d509a45d5cb04
7
+ data.tar.gz: ca182780b4fcbaebac1def84ed5c8161b23049f2da2e2da98e3a071104a6c6a6a27f59fe6482e6b30875f498f4dffc527c1e1eb472c44724d428d97767295704
data/.DS_Store ADDED
Binary file
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+
4
+ Style/StringLiterals:
5
+ EnforcedStyle: double_quotes
6
+
7
+ Style/StringLiteralsInInterpolation:
8
+ EnforcedStyle: double_quotes
data/.yardopts ADDED
@@ -0,0 +1,11 @@
1
+ --readme README.md
2
+ --title "Inkpen - TipTap Rich Text Editor for Rails"
3
+ --markup rdoc
4
+ --output-dir doc
5
+ --protected
6
+ --no-private
7
+ --hide-void-return
8
+ --exclude test/
9
+ --exclude spec/
10
+ lib/**/*.rb
11
+ app/helpers/**/*.rb
data/CLAUDE.md ADDED
@@ -0,0 +1,141 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code when working with the Inkpen gem.
4
+
5
+ ## Project Overview
6
+
7
+ Inkpen is a TipTap-based rich text editor gem for Rails. It provides a customizable WYSIWYG editor with:
8
+ - BubbleMenu (floating toolbar on text selection)
9
+ - Fixed toolbar option
10
+ - Extensions system (mentions, tables, code blocks, task lists)
11
+ - Theme support (light/dark)
12
+
13
+ ## Usage
14
+
15
+ Inkpen is used for **Posts only** in MadeMySite. Pages use Monaco editor instead.
16
+
17
+ ## JavaScript/Stimulus Patterns (Fizzy Style)
18
+
19
+ **IMPORTANT:** Always follow Fizzy patterns from `/Users/naumantariq/Code/00-source/fizzy`.
20
+
21
+ ### Controller Structure
22
+
23
+ ```javascript
24
+ import { Controller } from "@hotwired/stimulus"
25
+ import { helperFunction } from "helpers/helper_name"
26
+
27
+ const CONSTANTS_HERE = 3000
28
+
29
+ export default class extends Controller {
30
+ static targets = ["element"]
31
+ static values = { url: String, delay: Number }
32
+
33
+ #privateField // Use # for private fields, NOT underscore
34
+
35
+ // Lifecycle
36
+
37
+ connect() { }
38
+ disconnect() { this.#cleanup() } // Always clean up
39
+
40
+ // Actions
41
+
42
+ actionMethod() { }
43
+
44
+ // Private
45
+
46
+ #privateMethod() { }
47
+ }
48
+ ```
49
+
50
+ ### Key Rules
51
+
52
+ - **Private fields**: Use `#field` NOT `_field`
53
+ - **Section comments**: `// Lifecycle`, `// Actions`, `// Private`
54
+ - **Always clean up**: Clear timers/observers in `disconnect()`
55
+ - **Values API**: Use `this.urlValue` NOT `getAttribute()`
56
+ - **Dispatch events**: For controller communication, NOT direct calls
57
+ - **Single responsibility**: Each controller does ONE thing well
58
+
59
+ ### Helper Files
60
+
61
+ ```javascript
62
+ // ✅ Named function exports
63
+ export function throttle(fn, delay = 1000) { }
64
+ export function debounce(fn, delay = 1000) { }
65
+
66
+ // ❌ DON'T use object exports
67
+ export const Helpers = { throttle() { } }
68
+ ```
69
+
70
+ ### Import Pattern
71
+
72
+ ```javascript
73
+ // ✅ Named imports
74
+ import { throttle, debounce } from "helpers/timing_helpers"
75
+
76
+ // ❌ DON'T import objects
77
+ import { TimingHelpers } from "helpers/timing_helpers"
78
+ ```
79
+
80
+ ## Ruby Patterns
81
+
82
+ ### PORO Structure
83
+
84
+ ```ruby
85
+ module Inkpen
86
+ class SomeClass
87
+ attr_reader :option1, :option2
88
+
89
+ def initialize(option1:, option2: default)
90
+ @option1 = option1
91
+ @option2 = option2
92
+ end
93
+
94
+ def some_method
95
+ # Implementation
96
+ end
97
+
98
+ private
99
+
100
+ def private_method
101
+ # Private logic
102
+ end
103
+ end
104
+ end
105
+ ```
106
+
107
+ ## File Structure
108
+
109
+ ```
110
+ lib/inkpen/
111
+ ├── editor.rb # Main editor PORO
112
+ ├── toolbar.rb # Toolbar configuration
113
+ ├── configuration.rb # Global config
114
+ ├── extensions/ # TipTap extension configs
115
+ │ ├── base.rb
116
+ │ ├── mention.rb
117
+ │ ├── table.rb
118
+ │ └── task_list.rb
119
+ └── engine.rb # Rails engine
120
+
121
+ app/
122
+ ├── assets/
123
+ │ ├── javascripts/inkpen/
124
+ │ │ ├── controllers/
125
+ │ │ │ ├── editor_controller.js
126
+ │ │ │ └── toolbar_controller.js
127
+ │ │ └── index.js
128
+ │ └── stylesheets/inkpen/
129
+ └── helpers/inkpen/
130
+ └── editor_helper.rb
131
+ ```
132
+
133
+ ## Development
134
+
135
+ ```bash
136
+ # Run tests
137
+ bundle exec rake test
138
+
139
+ # Console
140
+ bin/console
141
+ ```
data/README.md ADDED
@@ -0,0 +1,409 @@
1
+ # Inkpen
2
+
3
+ <a href="https://builtonrails.com/@nauman"><img src="https://builtonrails.com/badges/inkpen.svg" alt="Built on Rails"></a>
4
+
5
+ A modern, TipTap-based rich text editor for Ruby on Rails applications. Inkpen provides a clean, extensible editor with a PORO-based architecture that seamlessly integrates with Rails forms and Hotwire/Stimulus.
6
+
7
+ ## Features
8
+
9
+ - **TipTap/ProseMirror Foundation**: Built on the powerful TipTap editor framework
10
+ - **Rails Integration**: Works seamlessly with Rails forms, Turbo, and Stimulus
11
+ - **PORO Architecture**: Clean Ruby objects for configuration and extensions
12
+ - **Importmap Compatible**: No Node.js build step required
13
+ - **Extensible**: Modular extension system for adding features
14
+ - **Toolbar Options**: Floating, fixed, or hidden toolbar configurations
15
+
16
+ ## Installation
17
+
18
+ Add to your Gemfile:
19
+
20
+ ```ruby
21
+ gem "inkpen", github: "nauman/inkpen"
22
+ ```
23
+
24
+ Then run:
25
+
26
+ ```bash
27
+ bundle install
28
+ ```
29
+
30
+ ## Configuration
31
+
32
+ Configure Inkpen globally in an initializer:
33
+
34
+ ```ruby
35
+ # config/initializers/inkpen.rb
36
+
37
+ Inkpen.configure do |config|
38
+ config.toolbar = :floating # :floating, :fixed, :none
39
+ config.placeholder = "Start writing..."
40
+ config.autosave = true
41
+ config.autosave_interval = 5000 # milliseconds
42
+ config.min_height = "200px"
43
+ config.max_height = "600px"
44
+
45
+ # Enable/disable extensions
46
+ config.extensions = [:bold, :italic, :link, :heading, :bullet_list]
47
+ end
48
+ ```
49
+
50
+ ## Basic Usage
51
+
52
+ ### Creating an Editor Instance
53
+
54
+ ```ruby
55
+ # In your controller or view
56
+ editor = Inkpen::Editor.new(
57
+ name: "post[body]",
58
+ value: @post.body,
59
+ toolbar: :floating,
60
+ extensions: [:bold, :italic, :link, :heading, :mentions],
61
+ placeholder: "Write your post..."
62
+ )
63
+ ```
64
+
65
+ ### In Views (ERB)
66
+
67
+ ```erb
68
+ <%= tag.div editor.data_attributes do %>
69
+ <%= hidden_field_tag editor.input_name, editor.value %>
70
+ <div class="inkpen-editor" style="<%= editor.style_attributes %>"></div>
71
+ <% end %>
72
+ ```
73
+
74
+ ### Toolbar Configuration
75
+
76
+ ```ruby
77
+ toolbar = Inkpen::Toolbar.new(
78
+ style: :floating,
79
+ buttons: [:bold, :italic, :link, :heading],
80
+ position: :top
81
+ )
82
+
83
+ # Predefined button presets
84
+ Inkpen::Toolbar::PRESET_MINIMAL # [:bold, :italic, :link]
85
+ Inkpen::Toolbar::PRESET_STANDARD # Formatting + common blocks
86
+ Inkpen::Toolbar::PRESET_FULL # All available buttons
87
+ ```
88
+
89
+ ### Sticky Toolbar
90
+
91
+ The sticky toolbar provides a fixed-position toolbar for inserting blocks, media, and widgets. It supports horizontal (bottom) and vertical (left/right) positions.
92
+
93
+ ```ruby
94
+ # Enable sticky toolbar with default settings
95
+ editor = Inkpen::Editor.new(
96
+ name: "post[body]",
97
+ value: @post.body,
98
+ sticky_toolbar: Inkpen::StickyToolbar.new(
99
+ position: :bottom, # :bottom, :left, :right
100
+ buttons: [:table, :code_block, :image, :youtube, :widget],
101
+ widget_types: %w[form gallery poll]
102
+ )
103
+ )
104
+ ```
105
+
106
+ **Available buttons:**
107
+
108
+ | Button | Description |
109
+ |--------|-------------|
110
+ | `table` | Insert a table |
111
+ | `code_block` | Insert code block |
112
+ | `blockquote` | Insert quote block |
113
+ | `horizontal_rule` | Insert divider line |
114
+ | `task_list` | Insert task list |
115
+ | `image` | Insert image (triggers `inkpen:request-image` event) |
116
+ | `youtube` | Insert YouTube video |
117
+ | `embed` | Insert embed (triggers `inkpen:request-embed` event) |
118
+ | `widget` | Open widget picker modal |
119
+ | `divider` | Visual separator |
120
+
121
+ **Presets:**
122
+
123
+ ```ruby
124
+ Inkpen::StickyToolbar::PRESET_BLOCKS # table, code_block, blockquote, etc.
125
+ Inkpen::StickyToolbar::PRESET_MEDIA # image, youtube, embed
126
+ Inkpen::StickyToolbar::PRESET_FULL # All buttons
127
+ ```
128
+
129
+ **Handling widget events:**
130
+
131
+ ```javascript
132
+ // In your application.js or page-specific controller
133
+ document.addEventListener("inkpen:insert-widget", (event) => {
134
+ const { type, controller } = event.detail
135
+ // type is "form", "gallery", or "poll"
136
+ // Show your widget picker UI
137
+ })
138
+
139
+ document.addEventListener("inkpen:request-image", (event) => {
140
+ const { controller } = event.detail
141
+ // Show image upload modal
142
+ // Then call: controller.insertImage(url, altText)
143
+ })
144
+ ```
145
+
146
+ ## Extensions
147
+
148
+ Inkpen uses a modular extension system. Each extension is a PORO that configures TipTap extensions.
149
+
150
+ ### Core Extensions
151
+
152
+ Available by default:
153
+
154
+ - `bold`, `italic`, `strike`, `underline`
155
+ - `link`, `heading`
156
+ - `bullet_list`, `ordered_list`
157
+ - `blockquote`, `code_block`
158
+ - `horizontal_rule`, `hard_break`
159
+
160
+ ### Advanced Extensions
161
+
162
+ #### Forced Document Structure
163
+
164
+ Enforces a document structure with a required title heading:
165
+
166
+ ```ruby
167
+ extension = Inkpen::Extensions::ForcedDocument.new(
168
+ heading_level: 1,
169
+ placeholder: "Enter your title...",
170
+ allow_deletion: false
171
+ )
172
+
173
+ extension.to_config
174
+ # => { headingLevel: 1, titlePlaceholder: "Enter your title...", ... }
175
+ ```
176
+
177
+ #### Mentions
178
+
179
+ Enable @mentions functionality:
180
+
181
+ ```ruby
182
+ extension = Inkpen::Extensions::Mention.new(
183
+ search_url: "/api/users/search",
184
+ trigger: "@",
185
+ min_chars: 1,
186
+ suggestion_class: "mention-popup",
187
+ allow_custom: false
188
+ )
189
+
190
+ # Or with static items:
191
+ extension = Inkpen::Extensions::Mention.new(
192
+ items: [
193
+ { id: 1, label: "John Doe" },
194
+ { id: 2, label: "Jane Smith" }
195
+ ]
196
+ )
197
+ ```
198
+
199
+ #### Code Block with Syntax Highlighting
200
+
201
+ Add syntax highlighting to code blocks:
202
+
203
+ ```ruby
204
+ extension = Inkpen::Extensions::CodeBlockSyntax.new(
205
+ languages: [:ruby, :javascript, :python, :sql],
206
+ default_language: :ruby,
207
+ line_numbers: true,
208
+ language_selector: true,
209
+ copy_button: true,
210
+ theme: "github" # or "monokai", "dracula"
211
+ )
212
+ ```
213
+
214
+ Available languages: `javascript`, `typescript`, `ruby`, `python`, `css`, `xml`, `html`, `json`, `bash`, `sql`, `markdown`, `go`, `rust`, `java`, `kotlin`, `swift`, `php`, `c`, `cpp`, `csharp`, `elixir`, and more.
215
+
216
+ #### Tables
217
+
218
+ Add table support with resizing and toolbar:
219
+
220
+ ```ruby
221
+ extension = Inkpen::Extensions::Table.new(
222
+ resizable: true,
223
+ header_row: true,
224
+ header_column: false,
225
+ cell_min_width: 25,
226
+ toolbar: true,
227
+ allow_merge: true,
228
+ default_rows: 3,
229
+ default_cols: 3
230
+ )
231
+ ```
232
+
233
+ #### Task Lists
234
+
235
+ Add interactive checkboxes/task lists:
236
+
237
+ ```ruby
238
+ extension = Inkpen::Extensions::TaskList.new(
239
+ nested: true,
240
+ list_class: "task-list",
241
+ item_class: "task-item",
242
+ checked_class: "task-checked",
243
+ keyboard_shortcut: "Mod-Shift-9"
244
+ )
245
+ ```
246
+
247
+ ### Creating Custom Extensions
248
+
249
+ Extend `Inkpen::Extensions::Base`:
250
+
251
+ ```ruby
252
+ module Inkpen
253
+ module Extensions
254
+ class MyCustomExtension < Base
255
+ def name
256
+ :my_custom
257
+ end
258
+
259
+ def to_config
260
+ {
261
+ optionOne: options.fetch(:option_one, "default"),
262
+ optionTwo: options.fetch(:option_two, true)
263
+ }
264
+ end
265
+
266
+ private
267
+
268
+ def default_options
269
+ super.merge(
270
+ option_one: "default",
271
+ option_two: true
272
+ )
273
+ end
274
+ end
275
+ end
276
+ end
277
+ ```
278
+
279
+ ## JavaScript Integration
280
+
281
+ Inkpen uses Stimulus controllers and importmaps. The gem automatically registers pins for TipTap and ProseMirror dependencies.
282
+
283
+ ### Required Importmap Pins
284
+
285
+ The gem includes pins for:
286
+
287
+ - TipTap core and PM adapters
288
+ - ProseMirror packages
289
+ - TipTap extensions (document, paragraph, text, formatting, etc.)
290
+ - Lowlight for syntax highlighting
291
+ - Highlight.js language definitions
292
+
293
+ ### Stimulus Controller
294
+
295
+ The editor is controlled by `inkpen--editor` Stimulus controller. Connect it to your editor container:
296
+
297
+ ```html
298
+ <div data-controller="inkpen--editor"
299
+ data-inkpen--editor-extensions-value='["bold","italic","link"]'
300
+ data-inkpen--editor-toolbar-value="floating"
301
+ data-inkpen--editor-placeholder-value="Start writing...">
302
+ <!-- Editor content here -->
303
+ </div>
304
+ ```
305
+
306
+ ## Architecture
307
+
308
+ ```
309
+ inkpen/
310
+ ├── lib/
311
+ │ ├── inkpen.rb # Main entry point
312
+ │ ├── inkpen/
313
+ │ │ ├── configuration.rb # Global config PORO
314
+ │ │ ├── editor.rb # Editor instance PORO
315
+ │ │ ├── toolbar.rb # Floating toolbar config PORO
316
+ │ │ ├── sticky_toolbar.rb # Sticky toolbar config PORO
317
+ │ │ ├── engine.rb # Rails engine
318
+ │ │ ├── version.rb
319
+ │ │ └── extensions/
320
+ │ │ ├── base.rb # Extension base class
321
+ │ │ ├── forced_document.rb # Title heading structure
322
+ │ │ ├── mention.rb # @mentions
323
+ │ │ ├── code_block_syntax.rb # Syntax highlighting
324
+ │ │ ├── table.rb # Table support
325
+ │ │ └── task_list.rb # Task/checkbox lists
326
+ ├── app/
327
+ │ └── assets/
328
+ │ ├── javascripts/
329
+ │ │ └── inkpen/
330
+ │ │ ├── controllers/
331
+ │ │ │ ├── editor_controller.js # Main TipTap editor
332
+ │ │ │ ├── toolbar_controller.js # Floating toolbar
333
+ │ │ │ └── sticky_toolbar_controller.js # Sticky toolbar
334
+ │ │ └── index.js # Entry point
335
+ │ └── stylesheets/
336
+ │ └── inkpen/
337
+ │ ├── editor.css # Editor styles
338
+ │ └── sticky_toolbar.css # Sticky toolbar styles
339
+ ├── config/
340
+ │ └── importmap.rb # TipTap/PM dependencies
341
+ └── README.md
342
+ ```
343
+
344
+ ## API Reference
345
+
346
+ ### Inkpen::Editor
347
+
348
+ | Method | Description |
349
+ |--------|-------------|
350
+ | `name` | Form field name |
351
+ | `value` | Current editor content |
352
+ | `toolbar` | Toolbar style (`:floating`, `:fixed`, `:none`) |
353
+ | `extensions` | Array of enabled extension symbols |
354
+ | `data_attributes` | Hash of Stimulus data attributes |
355
+ | `style_attributes` | CSS inline styles string |
356
+ | `extension_enabled?(name)` | Check if extension is enabled |
357
+ | `input_id` | Safe HTML ID from name |
358
+
359
+ ### Inkpen::Toolbar
360
+
361
+ | Method | Description |
362
+ |--------|-------------|
363
+ | `style` | Toolbar style |
364
+ | `buttons` | Array of button symbols |
365
+ | `position` | Toolbar position (`:top`, `:bottom`) |
366
+ | `floating?` | Is floating toolbar? |
367
+ | `fixed?` | Is fixed toolbar? |
368
+ | `hidden?` | Is toolbar hidden? |
369
+
370
+ ### Inkpen::StickyToolbar
371
+
372
+ | Method | Description |
373
+ |--------|-------------|
374
+ | `position` | Position (`:bottom`, `:left`, `:right`) |
375
+ | `buttons` | Array of button symbols |
376
+ | `widget_types` | Array of widget type strings |
377
+ | `enabled?` | Is sticky toolbar enabled? |
378
+ | `vertical?` | Is vertical layout (left/right)? |
379
+ | `horizontal?` | Is horizontal layout (bottom)? |
380
+ | `data_attributes` | Hash of Stimulus data attributes |
381
+
382
+ ### Inkpen::Extensions::Base
383
+
384
+ | Method | Description |
385
+ |--------|-------------|
386
+ | `name` | Extension identifier (Symbol) |
387
+ | `enabled?` | Is extension enabled? |
388
+ | `options` | Configuration options hash |
389
+ | `to_config` | JS configuration hash |
390
+ | `to_h` | Full extension hash |
391
+ | `to_json` | JSON representation |
392
+
393
+ ## Development
394
+
395
+ After checking out the repo:
396
+
397
+ ```bash
398
+ bin/setup # Install dependencies
399
+ bundle exec rake test # Run tests
400
+ bin/console # Interactive console
401
+ ```
402
+
403
+ ## Contributing
404
+
405
+ Bug reports and pull requests are welcome on GitHub.
406
+
407
+ ## License
408
+
409
+ MIT License
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ require "yard"
13
+
14
+ YARD::Rake::YardocTask.new do |t|
15
+ t.files = ["lib/**/*.rb", "app/helpers/**/*.rb"]
16
+ t.options = ["--title", "Inkpen - TipTap Rich Text Editor for Rails"]
17
+ end
18
+
19
+ task default: %i[test rubocop]