lexxy 0.1.1.beta
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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +289 -0
- data/Rakefile +8 -0
- data/app/assets/javascript/lexxy.js +8463 -0
- data/app/assets/javascript/lexxy.js.br +0 -0
- data/app/assets/javascript/lexxy.js.gz +0 -0
- data/app/assets/javascript/lexxy.min.js +10 -0
- data/app/assets/javascript/lexxy.min.js.br +0 -0
- data/app/assets/javascript/lexxy.min.js.gz +0 -0
- data/app/assets/stylesheets/lexxy-content.css +347 -0
- data/app/assets/stylesheets/lexxy-editor.css +255 -0
- data/app/assets/stylesheets/lexxy-variables.css +51 -0
- data/app/assets/stylesheets/lexxy.css +2 -0
- data/app/controllers/actiontext/lexical/application_controller.rb +6 -0
- data/app/helpers/actiontext/lexical/application_helper.rb +6 -0
- data/app/jobs/actiontext/lexical/application_job.rb +6 -0
- data/app/mailers/actiontext/lexical/application_mailer.rb +8 -0
- data/app/models/actiontext/lexical/application_record.rb +7 -0
- data/app/views/layouts/actiontext/lexical/application.html.erb +17 -0
- data/app/views/people/_person.html.erb +1 -0
- data/app/views/people/_prompt_item.html.erb +6 -0
- data/app/views/people/index.html.erb +3 -0
- data/config/routes.rb +2 -0
- data/lib/active_storage/blob_with_preview_url.rb +18 -0
- data/lib/lexxy/action_text_tag.rb +18 -0
- data/lib/lexxy/engine.rb +43 -0
- data/lib/lexxy/form_builder.rb +9 -0
- data/lib/lexxy/form_helper.rb +9 -0
- data/lib/lexxy/rich_text_area_tag.rb +38 -0
- data/lib/lexxy/version.rb +3 -0
- data/lib/lexxy.rb +6 -0
- data/lib/tasks/actiontext/lexical_tasks.rake +4 -0
- metadata +173 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f9a5f63e546cae79c0bd74925847e357ad566838d983739a9ab2724ef0df952f
|
4
|
+
data.tar.gz: 9c1660f8c7977ac0dc53a810e2e5fd954e1994e388cd852324b52fafa42dd291
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: df6a603e766dee3e7103266e7472576072dbbd2338810b3f72eb2ed2898e15f29f0075c9f3bc582c936b634600c982eb2dd37db64578d79404fa7798ea83e31f
|
7
|
+
data.tar.gz: 9bfb5aac0c12f228425bc907a2d3598a3058835f2ca1d9b0557b8f9667126c93805bde1d55be8e91042ec93d653be7db01c1e7119edc8d5ff1ab97552baed2f4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright Jorge Manrubia
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
# Lexxy
|
2
|
+
|
3
|
+
A modern rich text editor for Rails.
|
4
|
+
|
5
|
+
> [!IMPORTANT]
|
6
|
+
> This is an early beta. It hasn't been battle-tested yet. Please try it out and report any issues you find.
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- Built on top of [Lexical](https://lexical.dev), the powerful text editor framework from Meta.
|
11
|
+
- Good HTML semantics. Paragraphs are real `<p>` tags, as they should be.
|
12
|
+
- Markdown support: shortcuts, auto-formatting on paste.
|
13
|
+
- Real-time code syntax highlighting.
|
14
|
+
- Create links by pasting URLs on selected text.
|
15
|
+
- Configurable prompts. Support for mentions and other interactive prompts with multiple loading and filtering strategies.
|
16
|
+
- Preview attachments like PDFs and Videos in the editor.
|
17
|
+
- Works seamlessly with Action Text, generating the same canonical HTML format it expects for attachments.
|
18
|
+
|
19
|
+

|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'lexxy'
|
27
|
+
```
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
```bash
|
32
|
+
bundle install
|
33
|
+
```
|
34
|
+
|
35
|
+
Then, you need to import the lexxy source in your app. If you are using [propshaft](https://github.com/rails/propshaft) and [import maps](https://github.com/rails/importmap-rails) you can do:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
# importmap.rb
|
39
|
+
pin "lexxy", to: "lexxy.js"
|
40
|
+
```
|
41
|
+
|
42
|
+
```javascript
|
43
|
+
// application.js
|
44
|
+
import "lexxy"
|
45
|
+
```
|
46
|
+
|
47
|
+
For the CSS, you can include it with the standard Rails helper:
|
48
|
+
|
49
|
+
```erb
|
50
|
+
<%= stylesheet_link_tag "lexxy" %>
|
51
|
+
```
|
52
|
+
|
53
|
+
For applying the same styles to rendered Action Text content, you need to override the current default by adding this template `app/views/layouts/action_text/contents/_content.html.erb`:
|
54
|
+
|
55
|
+
```erb
|
56
|
+
<div class="lexxy-content">
|
57
|
+
<%= yield -%>
|
58
|
+
</div>
|
59
|
+
```
|
60
|
+
|
61
|
+
Of course, you can copy the CSS to your project and adapt it to your needs.
|
62
|
+
|
63
|
+
> [!NOTE]
|
64
|
+
> We'll streamline the configuration process as we work towards a final release.
|
65
|
+
|
66
|
+
## Configuration
|
67
|
+
|
68
|
+
You can add a Lexxy instance using the regular Action Text form helper:
|
69
|
+
|
70
|
+
```erb
|
71
|
+
<%= form_with model: @post do |form| %>
|
72
|
+
<%= form.rich_text_area :content %>
|
73
|
+
<% end %>
|
74
|
+
```
|
75
|
+
|
76
|
+
Under the hood, this will insert a `<lexxy-editor>` tag, that will be a first-class form control:
|
77
|
+
|
78
|
+
```html
|
79
|
+
<lexxy-editor name="post[body]"...>...</lexxy-editor>
|
80
|
+
```
|
81
|
+
|
82
|
+
## Options
|
83
|
+
|
84
|
+
The `<lexxy-editor>` element supports these options:
|
85
|
+
|
86
|
+
- `placeholder` - Text displayed when the editor is empty.
|
87
|
+
- `toolbar` - Pass `"false"` to disable the toolbar entirely, or pass an element ID to render the toolbar in an external element. By default, the toolbar is bootstrapped and displayed above the editor.
|
88
|
+
- `attachments` - Pass `"false"` to disable attachments completely. By default, attachments are supported, including paste and Drag & Drop support.
|
89
|
+
|
90
|
+
Lexxy uses the `ElementInternals` API to participate in HTML forms as any standard control. This means that you can use standard HTML attributes like `name`, `value`, `required`, `disabled`, etc.
|
91
|
+
|
92
|
+
## Prompts
|
93
|
+
|
94
|
+
Prompts let you implement features like @mentions, /commands, or any other trigger-based suggestions. When you select an item from the prompt, you have two options:
|
95
|
+
|
96
|
+
1. Insert the item as an [Action Text custom attachment](https://guides.rubyonrails.org/action_text_overview.html#signed-globalid). This allows you to use standard Action Text to customize how it renders or processes them on the server side.
|
97
|
+
2. Insert the item as free text in the editor.
|
98
|
+
|
99
|
+
Lexxy also lets you configure how to load the items: inline or remotely, and how to do the filtering (locally or on the server).
|
100
|
+
|
101
|
+
### General setup
|
102
|
+
|
103
|
+
The first thing to do is to add a `<lexxy-prompt>` element to the editor:
|
104
|
+
|
105
|
+
```erb
|
106
|
+
<%= form_with model: @post do |form| %>
|
107
|
+
<lexxy-prompt trigger="@">
|
108
|
+
</lexxy-prompt>
|
109
|
+
<% end %>
|
110
|
+
```
|
111
|
+
|
112
|
+
The `trigger` option determines which key will open the prompt. A prompt can load its items from two sources:
|
113
|
+
|
114
|
+
- Inline, by defining the items inside the `<lexxy-prompt>` element.
|
115
|
+
- Remotely, by setting a `src` attribute with an endpoint to load the items.
|
116
|
+
|
117
|
+
Regardless of the source, the prompt items are defined using `<lexxy-prompt-item>` elements. A basic prompt item looks like this:
|
118
|
+
|
119
|
+
```html
|
120
|
+
<lexxy-prompt-item search="...">
|
121
|
+
<template type="menu">...</template>
|
122
|
+
<template type="editor">
|
123
|
+
...
|
124
|
+
</template>
|
125
|
+
</lexxy-prompt-item>
|
126
|
+
```
|
127
|
+
|
128
|
+
Where:
|
129
|
+
|
130
|
+
* `search` contains the text to match against when filtering.
|
131
|
+
* `template[type= "menu"]` defines how the item appears in the dropdown menu.
|
132
|
+
* `template[type= "editor"]` defines how the item appears in the editor when selected.
|
133
|
+
|
134
|
+
### Custom attachments with inline loading
|
135
|
+
|
136
|
+
Imagine you want to implement a *mentions* feature, where users can type "@" and select a person to mention. You want to save mentions as action text attachments for further server-side processing when the form is submitted.
|
137
|
+
|
138
|
+
First, you need to include the `ActionText::Attachable` concern in your model.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
# app/models/person.rb
|
142
|
+
class Person < ApplicationRecord
|
143
|
+
include ActionText::Attachable
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
147
|
+
By default, the partial to render the attachment will be looked up in `app/views/[model plural]/_[model singular].html.erb`. You can customize this by implementing `#to_attachable_partial_path` in the model. Let's go with the default and render a simple view that renders the person's name and initials:
|
148
|
+
|
149
|
+
```erb
|
150
|
+
# app/views/people/_person.html.erb
|
151
|
+
<em><%= person.name %></em> (<strong><%= person.initials %></strong>)
|
152
|
+
```
|
153
|
+
|
154
|
+
On the editor side, let's start with the *inline* approach by rendering all the prompt items inside the `<lexxy-prompt>` element:
|
155
|
+
|
156
|
+
```erb
|
157
|
+
<%= form.rich_text_area :body do %>
|
158
|
+
<lexxy-prompt trigger="@" name="mention">
|
159
|
+
<%= render partial: "people/prompt_item", collection: Person.all, as: :person %>
|
160
|
+
</lexxy-prompt>
|
161
|
+
<% end %>
|
162
|
+
```
|
163
|
+
|
164
|
+
With `app/views/people/_prompt_item.html.erb` defining each prompt item:
|
165
|
+
|
166
|
+
```erb
|
167
|
+
<lexxy-prompt-item search="<%= "#{person.name} #{person.initials}" %>" sgid="<%= person.attachable_sgid %>">
|
168
|
+
<template type="menu"><%= person.name %></template>
|
169
|
+
<template type="editor">
|
170
|
+
<%= render "people/person", person: person %>
|
171
|
+
</template>
|
172
|
+
</lexxy-prompt-item>
|
173
|
+
```
|
174
|
+
|
175
|
+
Notice how the template for rendering the editor representation (`type=" editor") uses the same template as the attachment partial. This way, you ensure consistency between how the mention looks in the editor and how it will render when displaying the text in view mode with Action Text.
|
176
|
+
|
177
|
+
Two important additional notes to use action text with custom attachments:
|
178
|
+
|
179
|
+
* Each `<lexxy-prompt-item>` must include a `sgid` attribute with the [global id that Action Text will use to find the associated model](https://guides.rubyonrails.org/action_text_overview.html#signed-globalid).
|
180
|
+
* The `<lexxy-prompt>` must include a `name` attribute that will determine the content type of the attachment. For example, for `name= "mention"`, the attachment will be saved as `application/vnd.actiontext.mention`.
|
181
|
+
|
182
|
+
### Custom attachments with remote loading
|
183
|
+
|
184
|
+
For moderately large sets, you can configure Lexxy to load all the options from a remote endpoint once, and filter them locally as the user types. This is a good balance between performance and responsiveness.
|
185
|
+
|
186
|
+
Continuing with the mentions example, we could have a controller action that returns all people as prompt items, and configure it as the remote source via the `src` attribute:
|
187
|
+
|
188
|
+
```erb
|
189
|
+
<lexxy-prompt trigger="@" src="<%= people_path %>" name="mention">
|
190
|
+
</lexxy-prompt>
|
191
|
+
```
|
192
|
+
|
193
|
+
We could define the controller action to serve the prompt items like this:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
class PeopleController < ApplicationController
|
197
|
+
def index
|
198
|
+
@people = Person.all
|
199
|
+
|
200
|
+
render layout: false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
205
|
+
And the action would just list the prompt items:
|
206
|
+
|
207
|
+
```erb
|
208
|
+
<% @people.each do |person| %>
|
209
|
+
<%= render "people/prompt_item", person: person %>
|
210
|
+
<% end %>
|
211
|
+
```
|
212
|
+
|
213
|
+
### Free HTML attachments
|
214
|
+
|
215
|
+
If you don't want to use custom action text attachments, you can configure prompts to simply insert the prompt item HTML directly in the editor. This is useful for things like hashtags, emojis, or other inline elements that don't require server-side processing.
|
216
|
+
|
217
|
+
To enable these, you must add the `insert-editable-text` attribute to the `<lexxy-prompt>` element:
|
218
|
+
|
219
|
+
```erb
|
220
|
+
<lexxy-prompt trigger="@" src="<%= people_path %>" insert-editable-text>
|
221
|
+
</lexxy-prompt>
|
222
|
+
```
|
223
|
+
|
224
|
+
When configured like this,if you select an item from the prompt, the content of the `template[type= "editor"]` will be inserted directly in the editor as HTML you can edit freely, instead of as an `<action-text-attachment>` element. Notice that in this case, you need to make sure that the HTML is compatible with the tags that Lexxy supports.
|
225
|
+
|
226
|
+
### Remote filtering
|
227
|
+
|
228
|
+
There are scenarios where you want to query the server for filtering, instead of loading all options at once. This is useful for large datasets or complex searches. In this case, you must add the `remote-filtering` attribute to the `<lexxy-prompt>` element:
|
229
|
+
|
230
|
+
```erb
|
231
|
+
<lexxy-prompt trigger="@" src="<%= people_path %>" name="mention" remote-filtering>
|
232
|
+
</lexxy-prompt>
|
233
|
+
```
|
234
|
+
|
235
|
+
By default, the `SPACE` key will select the current item in the prompt. If you want to allow spaces in the search query, you can add the `supports-space-in-searches` attribute to the prompt. This can be handy to search by full names in combination with remote filtering.
|
236
|
+
|
237
|
+
### Prompt Options reference
|
238
|
+
|
239
|
+
#### `<lexxy-prompt>`
|
240
|
+
|
241
|
+
- `trigger` - The character that activates the prompt (e.g., "@", "#", "/").
|
242
|
+
- `src` - Path or URL to load items remotely.
|
243
|
+
- `name` - Identifier for the prompt type (determines attachment content type, e.g., `name= "mention"` creates `application/vnd.actiontext.mention`). Mandatory unless using `insert-editable-text`.
|
244
|
+
- `empty-results` - Message shown when no matches found. By default it is "Nothing found".
|
245
|
+
- `remote-filtering` - Enable server-side filtering instead of loading all options at once.
|
246
|
+
- `insert-editable-text` - Insert prompt item HTML directly as editable text instead of Action Text attachments.
|
247
|
+
- `supports-space-in-searches` - Allow spaces in search queries (useful with remote filtering for full name searches).
|
248
|
+
|
249
|
+
#### `<lexxy-prompt-item>`
|
250
|
+
|
251
|
+
- `search` - The text to match against when filtering (can include multiple fields for better search).
|
252
|
+
- `sgid` - The signed GlobalID for Action Text attachments (use `attachable_sgid` helper). Mandatory unless using `insert-editable-text`.
|
253
|
+
|
254
|
+
## Roadmap
|
255
|
+
|
256
|
+
This is an early beta. Here's what's coming next:
|
257
|
+
|
258
|
+
- Configurable editors in Action Text - Choose your editor like you choose your database.
|
259
|
+
- More editing features:
|
260
|
+
- Tables
|
261
|
+
- Text highlighting
|
262
|
+
- Image galleries - The only remaining feature for full Action Text compatibility
|
263
|
+
- Install task that generates the necessary JS and adds stylesheets.
|
264
|
+
- Standalone JS package - to use in non-Rails environments.
|
265
|
+
|
266
|
+
## Development
|
267
|
+
|
268
|
+
To build the JS source when it changes, run:
|
269
|
+
|
270
|
+
```bash
|
271
|
+
yarn build -w
|
272
|
+
```
|
273
|
+
|
274
|
+
To the sandbox app:
|
275
|
+
|
276
|
+
```bash
|
277
|
+
bin/rails server
|
278
|
+
```
|
279
|
+
|
280
|
+
The sandbox app is available at http://localhost:3000. There is also a CRUD example at http://localhost:3000/posts.
|
281
|
+
|
282
|
+
## Contributing
|
283
|
+
|
284
|
+
- Bug reports and pull requests are welcome on [GitHub Issues](https://github.com/basecamp/lexxy/issues). Help is especially welcome with [those tagged as "Help Wanted"](https://github.com/basecamp/lexxy/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22help%20wanted%22).
|
285
|
+
- For questions and general Lexxy discussion, please use the [Discussions section](https://github.com/basecamp/lexxy/discussions)
|
286
|
+
|
287
|
+
## License
|
288
|
+
|
289
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|