actiontext-syntax-highlighter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.md +20 -0
- data/README.md +100 -0
- data/Rakefile +38 -0
- data/app/assets/javascripts/action_text_syntax_highlighter.js +2 -0
- data/app/assets/javascripts/action_text_syntax_highlighter.js.map +1 -0
- data/app/controllers/action_text_syntax_highlighter/highlighted_code_blocks_controller.rb +31 -0
- data/app/frontend/action_text_syntax_highlighter/css/highlighted_code_block.css +39 -0
- data/app/frontend/action_text_syntax_highlighter/index.js +3 -0
- data/app/frontend/action_text_syntax_highlighter/javascript/actiontext_syntax_highlighter.js +17 -0
- data/app/frontend/action_text_syntax_highlighter/javascript/helpers.js +11 -0
- data/app/frontend/action_text_syntax_highlighter/javascript/highlighted_code_block.js +69 -0
- data/app/frontend/action_text_syntax_highlighter/javascript/trix_code_block_highlighter.js +81 -0
- data/app/jobs/action_text_syntax_highlighter/purge_deleted_highlighted_code_blocks_job.rb +11 -0
- data/app/models/action_text_syntax_highlighter/highlighted_code_block.rb +50 -0
- data/app/models/action_text_syntax_highlighter/highlighted_code_block/supported_languages.rb +11 -0
- data/app/views/action_text_syntax_highlighter/highlighted_code_blocks/_editor.html.erb +8 -0
- data/app/views/action_text_syntax_highlighter/highlighted_code_blocks/_highlighted_code_block.html.erb +4 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20210531195232_create_action_text_highlighted_code_blocks.rb +11 -0
- data/lib/action_text_syntax_highlighter.rb +17 -0
- data/lib/action_text_syntax_highlighter/engine.rb +32 -0
- data/lib/action_text_syntax_highlighter/has_highlighted_code_blocks.rb +14 -0
- data/lib/action_text_syntax_highlighter/pre_tags_to_highlighted_code_blocks_conversion.rb +32 -0
- data/lib/action_text_syntax_highlighter/version.rb +5 -0
- data/lib/tasks/action_text_syntax_highlighter_tasks.rake +18 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 31390bf8597bfd8d545a61a1682161e6e5e7096411cd89e1bcf981aa358eb0b2
|
4
|
+
data.tar.gz: be598fa01d142a066f9c736594f7b58e67cd26338f1349f4ae60ba531003c4d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05bf47fa9c3a47e153b51a63a532e806fe18d6b3aa8d68a49c0ad9c7d04613f794971f2bd3efa81020c0204a3a8900fbabc75187e430905195a98e9e375d3d78
|
7
|
+
data.tar.gz: 3d8c27f3a029bb7b13ec6e4b95ad1a3a39150632a41cc0e388f732a2b5348f8f9f61a0fa1c577d3e9c80b52c68401653886c5dc34f7b3590b1a5d62a46455feb
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Ayush Newatia
|
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,100 @@
|
|
1
|
+
# Description
|
2
|
+
|
3
|
+
`actiontext-syntax-highlighter` is an extension for `rails/actiontext` to allow the user to add language specified code blocks that are then highlighted on the server using [Rouge](http://github.com/rouge-ruby/rouge).
|
4
|
+
|
5
|
+
[**Check out the demo app to see how it works!**](https://actiontext-syntax-highlighter.herokuapp.com).
|
6
|
+
|
7
|
+
> Note: The experience is a bit flaky, especially on Safari. If you have any suggestions for how to improve it, please file an issue; or even better, open a Pull Request!
|
8
|
+
|
9
|
+
## How it works
|
10
|
+
|
11
|
+
This library intercepts the toolbar code button in the Trix editor and creates an attachment containing the HTML for an editable code block and language selection dropdown.
|
12
|
+
|
13
|
+
The changes to the code block are saved to the server every time edits are made. When the code block is being rendered out for viewing, it uses Rouge to highlight the content using the language selected in the dropdown; or attempting to guess the language if it's missing.
|
14
|
+
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Install ActionText if you haven't already done so:
|
19
|
+
|
20
|
+
```shell
|
21
|
+
$ bin/rails action_text:install
|
22
|
+
```
|
23
|
+
|
24
|
+
Then add this gem to your application's Gemfile:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
gem 'actiontext-syntax-highlighter', require: 'action_text_syntax_highlighter/engine'
|
28
|
+
```
|
29
|
+
|
30
|
+
And bundle and install it:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
$ bundle
|
34
|
+
$ bin/rails action_text_syntax_highlighter:install
|
35
|
+
```
|
36
|
+
|
37
|
+
This will copy the database migrations into place and install the Yarn module for the frontend code.
|
38
|
+
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
Add the following lines to your `application.js`:
|
43
|
+
|
44
|
+
```javascript
|
45
|
+
import { ActionTextSyntaxHighlighter } from "@ayushn21/actiontext-syntax-highlighter"
|
46
|
+
ActionTextSyntaxHighlighter.start()
|
47
|
+
```
|
48
|
+
|
49
|
+
Import the CSS for a theme from the `themes` directory:
|
50
|
+
|
51
|
+
```javascript
|
52
|
+
import "@ayushn21/actiontext-syntax-highlighter/themes/github.css"
|
53
|
+
```
|
54
|
+
|
55
|
+
The default theme is set to GitHub. If you wish to use another theme, import its CSS file and set the following config option in your `application.rb`:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
config.action_text_syntax_highlighter.default_theme = :base16
|
59
|
+
```
|
60
|
+
|
61
|
+
You can override the default theme by setting `@highlighted_code_block_theme` in your controller action where you're rendering out the rich text.
|
62
|
+
|
63
|
+
This plugin requires a global `Trix` variable to create attachments, so require Trix in your application as below:
|
64
|
+
|
65
|
+
```javascript
|
66
|
+
window.Trix = require("trix")
|
67
|
+
```
|
68
|
+
|
69
|
+
Finally, add a `data-highlights-code-blocks='true'` attribute to the Trix editor where you'd like to use this plugin:
|
70
|
+
|
71
|
+
```erb
|
72
|
+
<%= f.rich_text_area :content, data: { highlights_code_blocks: true } %>
|
73
|
+
```
|
74
|
+
|
75
|
+
### Bundled JavaScript
|
76
|
+
|
77
|
+
If you wish, you can import pre-bundled JavaScript instead of the module as described above:
|
78
|
+
|
79
|
+
```javascript
|
80
|
+
import "@ayushn21/actiontext-syntax-highlighter/dist"
|
81
|
+
```
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
1. Fork it (https://github.com/ayushn21/actiontext-syntax-highlighter/fork)
|
86
|
+
2. Clone the fork using `git clone` to your local development machine.
|
87
|
+
3. Create your feature branch (`git checkout -b my-new-feature`)
|
88
|
+
4. Commit your changes (`git commit -am 'Add some feature'`)
|
89
|
+
5. Push to the branch (`git push origin my-new-feature`)
|
90
|
+
6. Create a new Pull Request
|
91
|
+
|
92
|
+
> If you've made a change to the frontend code, please also run `rake frontend:build` before opening your PR!
|
93
|
+
|
94
|
+
## Info
|
95
|
+
|
96
|
+
This gem was extracted from [chapter24.app](https://chapter24.app)
|
97
|
+
|
98
|
+
|
99
|
+
## License
|
100
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
|
3
|
+
APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
|
4
|
+
load "rails/tasks/engine.rake"
|
5
|
+
load "rails/tasks/statistics.rake"
|
6
|
+
|
7
|
+
require "bundler/gem_tasks"
|
8
|
+
require "rake/testtask"
|
9
|
+
|
10
|
+
Rake::TestTask.new(:test) do |t|
|
11
|
+
t.libs << 'test'
|
12
|
+
t.pattern = 'test/**/*_test.rb'
|
13
|
+
t.verbose = false
|
14
|
+
t.warning = false
|
15
|
+
end
|
16
|
+
|
17
|
+
task default: :test
|
18
|
+
|
19
|
+
namespace :frontend do
|
20
|
+
task :transpile do
|
21
|
+
`bin/transpile`
|
22
|
+
end
|
23
|
+
|
24
|
+
task :bundle do
|
25
|
+
p "Bundling frontend for use in browser..."
|
26
|
+
|
27
|
+
`yarn webpack --mode production`
|
28
|
+
`cp dist/index.js app/assets/javascripts/action_text_syntax_highlighter.js`
|
29
|
+
`cp dist/index.js.map app/assets/javascripts/action_text_syntax_highlighter.js.map`
|
30
|
+
|
31
|
+
p "Done!"
|
32
|
+
end
|
33
|
+
|
34
|
+
task :build do
|
35
|
+
Rake::Task["frontend:transpile"].execute
|
36
|
+
Rake::Task["frontend:bundle"].execute
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,2 @@
|
|
1
|
+
(()=>{"use strict";var t={990:(t,e,n)=>{n.d(e,{Z:()=>r});var o=n(268),i=n.n(o)()(!0);i.push([t.id,"trix-editor pre[contenteditable] {\n -webkit-user-select: text;\n user-select: text;\n}\n\ntrix-editor pre[contenteditable]::selection {\n background: highlight;\n color: highlightText;\n}\n\ntrix-editor select[name='language'] {\n margin: 0.4em 0;\n font-size: 0.8em;\n padding: 0.4em 0;\n}\n\nhighlighted-code-block > pre {\n text-align: left;\n outline: none;\n}\n\nhighlighted-code-block > select {\n display: inline-block;\n float: right;\n}\n\nfigure[data-trix-mutable][data-trix-highlighted-code-block] pre {\n box-shadow: 0 0 0 1px highlight;\n}\n\nfigure[data-trix-highlighted-code-block] {\n width: 100%;\n}\n\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--text-tools > button,\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--block-tools > button,\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--file-tools > button {\n pointer-events: none;\n}\n","",{version:3,sources:["webpack://app/frontend/action_text_syntax_highlighter/css/highlighted_code_block.css"],names:[],mappings:"AAAA;EACE,yBAAyB;EACzB,iBAAiB;AACnB;;AAEA;EACE,qBAAqB;EACrB,oBAAoB;AACtB;;AAEA;EACE,eAAe;EACf,gBAAgB;EAChB,gBAAgB;AAClB;;AAEA;EACE,gBAAgB;EAChB,aAAa;AACf;;AAEA;EACE,qBAAqB;EACrB,YAAY;AACd;;AAEA;EACE,+BAA+B;AACjC;;AAEA;EACE,WAAW;AACb;;AAEA;;;EAGE,oBAAoB;AACtB",sourcesContent:["trix-editor pre[contenteditable] {\n -webkit-user-select: text;\n user-select: text;\n}\n\ntrix-editor pre[contenteditable]::selection {\n background: highlight;\n color: highlightText;\n}\n\ntrix-editor select[name='language'] {\n margin: 0.4em 0;\n font-size: 0.8em;\n padding: 0.4em 0;\n}\n\nhighlighted-code-block > pre {\n text-align: left;\n outline: none;\n}\n\nhighlighted-code-block > select {\n display: inline-block;\n float: right;\n}\n\nfigure[data-trix-mutable][data-trix-highlighted-code-block] pre {\n box-shadow: 0 0 0 1px highlight;\n}\n\nfigure[data-trix-highlighted-code-block] {\n width: 100%;\n}\n\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--text-tools > button,\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--block-tools > button,\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--file-tools > button {\n pointer-events: none;\n}\n"],sourceRoot:""}]);const r=i},268:t=>{t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var n=function(t,e){var n,o,i,r=t[1]||"",a=t[3];if(!a)return r;if(e&&"function"==typeof btoa){var s=(n=a,o=btoa(unescape(encodeURIComponent(JSON.stringify(n)))),i="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(o),"/*# ".concat(i," */")),l=a.sources.map((function(t){return"/*# sourceURL=".concat(a.sourceRoot||"").concat(t," */")}));return[r].concat(l).concat([s]).join("\n")}return[r].join("\n")}(e,t);return e[2]?"@media ".concat(e[2]," {").concat(n,"}"):n})).join("")},e.i=function(t,n,o){"string"==typeof t&&(t=[[null,t,""]]);var i={};if(o)for(var r=0;r<this.length;r++){var a=this[r][0];null!=a&&(i[a]=!0)}for(var s=0;s<t.length;s++){var l=[].concat(t[s]);o&&i[l[0]]||(n&&(l[2]?l[2]="".concat(n," and ").concat(l[2]):l[2]=n),e.push(l))}},e}},379:(t,e,n)=>{var o,i=function(){var t={};return function(e){if(void 0===t[e]){var n=document.querySelector(e);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(t){n=null}t[e]=n}return t[e]}}(),r=[];function a(t){for(var e=-1,n=0;n<r.length;n++)if(r[n].identifier===t){e=n;break}return e}function s(t,e){for(var n={},o=[],i=0;i<t.length;i++){var s=t[i],l=e.base?s[0]+e.base:s[0],c=n[l]||0,d="".concat(l," ").concat(c);n[l]=c+1;var h=a(d),u={css:s[1],media:s[2],sourceMap:s[3]};-1!==h?(r[h].references++,r[h].updater(u)):r.push({identifier:d,updater:p(u,e),references:1}),o.push(d)}return o}function l(t){var e=document.createElement("style"),o=t.attributes||{};if(void 0===o.nonce){var r=n.nc;r&&(o.nonce=r)}if(Object.keys(o).forEach((function(t){e.setAttribute(t,o[t])})),"function"==typeof t.insert)t.insert(e);else{var a=i(t.insert||"head");if(!a)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");a.appendChild(e)}return e}var c,d=(c=[],function(t,e){return c[t]=e,c.filter(Boolean).join("\n")});function h(t,e,n,o){var i=n?"":o.media?"@media ".concat(o.media," {").concat(o.css,"}"):o.css;if(t.styleSheet)t.styleSheet.cssText=d(e,i);else{var r=document.createTextNode(i),a=t.childNodes;a[e]&&t.removeChild(a[e]),a.length?t.insertBefore(r,a[e]):t.appendChild(r)}}function u(t,e,n){var o=n.css,i=n.media,r=n.sourceMap;if(i?t.setAttribute("media",i):t.removeAttribute("media"),r&&"undefined"!=typeof btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),t.styleSheet)t.styleSheet.cssText=o;else{for(;t.firstChild;)t.removeChild(t.firstChild);t.appendChild(document.createTextNode(o))}}var g=null,b=0;function p(t,e){var n,o,i;if(e.singleton){var r=b++;n=g||(g=l(e)),o=h.bind(null,n,r,!1),i=h.bind(null,n,r,!0)}else n=l(e),o=u.bind(null,n,e),i=function(){!function(t){if(null===t.parentNode)return!1;t.parentNode.removeChild(t)}(n)};return o(t),function(e){if(e){if(e.css===t.css&&e.media===t.media&&e.sourceMap===t.sourceMap)return;o(t=e)}else i()}}t.exports=function(t,e){(e=e||{}).singleton||"boolean"==typeof e.singleton||(e.singleton=(void 0===o&&(o=Boolean(window&&document&&document.all&&!window.atob)),o));var n=s(t=t||[],e);return function(t){if(t=t||[],"[object Array]"===Object.prototype.toString.call(t)){for(var o=0;o<n.length;o++){var i=a(n[o]);r[i].references--}for(var l=s(t,e),c=0;c<n.length;c++){var d=a(n[c]);0===r[d].references&&(r[d].updater(),r.splice(d,1))}n=l}}}}},e={};function n(o){var i=e[o];if(void 0!==i)return i.exports;var r=e[o]={id:o,exports:{}};return t[o](r,r.exports,n),r.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{class t extends HTMLElement{connectedCallback(){var t;(null==(t=this.closest("trix-editor"))?void 0:t.editor)&&this.setup()}setup(){this.editor=this.closest("trix-editor").editor,this.closest("figure").dataset.trixHighlightedCodeBlock=!0,this.trixId=parseInt(this.parentElement.dataset.trixId),this.attachment=this.editor.getDocument().getAttachmentById(this.trixId),this.attachment.type="HighlightedCodeBlock",this.configureEventListeners()}save(){let t=this.attachment.getAttributes().sgid;this.editor.syntaxHighlighter.saveCodeBlock(this.toJSON(),t),this.render()}render(){this.attachment.setAttributes({content:this.outerHTML})}languageChanged(t){this.selectLanguage(t.target.value),this.save()}selectLanguage(t){this.languageSelector.querySelector("option[selected]").removeAttribute("selected"),this.languageSelector.querySelector(`option[value='${t}']`).setAttribute("selected","")}configureEventListeners(){this.codeBlock.addEventListener("blur",this.save.bind(this)),this.languageSelector.addEventListener("change",this.languageChanged.bind(this))}toJSON(){return JSON.stringify({highlighted_code_block:{content:this.codeBlock.innerHTML,language:this.languageSelector.value}})}get codeBlock(){return this.querySelector("pre")}get languageSelector(){return this.querySelector("select")}}function e(){return{"Content-Type":"application/json",Accept:"application/json","X-CSRF-Token":document.head.querySelector("meta[name='csrf-token']").content}}class o{constructor(t){this.element=t,this.editor=t.editor,this.setup()}addCodeBlock(t){fetch(c.basePath,{method:"POST",headers:e()}).then((t=>t.json())).then((t=>this.insertCodeBlock(t)))}saveCodeBlock(t,n){fetch(`${c.basePath}/${n}`,{method:"PATCH",headers:e(),body:t})}insertCodeBlock(t){let e=new Trix.Attachment(t);this.editor.insertAttachment(e),this.editor.composition.editAttachment(e)}setup(){var t;this.editorToolbar.setAttribute("data-highlights-code-blocks",""),this.editor.syntaxHighlighter=this,this.interceptToolbarCodeButton(),this.watchCursor(),null==(t=this.element.querySelector("highlighted-code-block"))||t.setup()}interceptToolbarCodeButton(){this.editorToolbarCodeButton.addEventListener("mousedown",(t=>{this.addCodeBlock(),t.preventDefault(),t.stopImmediatePropagation()}))}watchCursor(){this.element.addEventListener("trix-selection-change",(t=>{this.updateToolbarState(this.editor.getPosition())}))}updateToolbarState(t){var e;"HighlightedCodeBlock"==(null==(e=this.editor.getDocument().getPieceAtPosition(t).attachment)?void 0:e.type)?(this.editorToolbarCodeButton.classList.add("trix-active"),this.editorToolbar.dataset.disableStylingInteraction=!0):(this.editorToolbarCodeButton.classList.remove("trix-active"),this.editorToolbar.removeAttribute("data-disable-styling-interaction"))}get editorToolbar(){return this.element.toolbarElement}get editorToolbarCodeButton(){return this.editorToolbar.querySelector("[data-trix-attribute='code']")}}var i=n(379),r=n.n(i),a=n(990);r()(a.Z,{insert:"head",singleton:!1}),a.Z.locals;var s,l=Object.defineProperty;class c{static start(){customElements.define("highlighted-code-block",t),document.addEventListener("trix-initialize",(t=>{t.target.dataset.highlightsCodeBlocks&&new o(t.target)}))}}((t,e,n)=>{e in t?l(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n})(c,"symbol"!=typeof(s="basePath")?s+"":s,"/rails/action_text/highlighted_code_blocks"),c.start()})()})();
|
2
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/css/highlighted_code_block.css","webpack://@ayushn21/actiontext-syntax-highlighter/./node_modules/css-loader/dist/runtime/api.js","webpack://@ayushn21/actiontext-syntax-highlighter/./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js","webpack://@ayushn21/actiontext-syntax-highlighter/webpack/bootstrap","webpack://@ayushn21/actiontext-syntax-highlighter/webpack/runtime/compat get default export","webpack://@ayushn21/actiontext-syntax-highlighter/webpack/runtime/define property getters","webpack://@ayushn21/actiontext-syntax-highlighter/webpack/runtime/hasOwnProperty shorthand","webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/javascript/highlighted_code_block.js","webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/javascript/helpers.js","webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/javascript/trix_code_block_highlighter.js","webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/css/highlighted_code_block.css?925a","webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/javascript/actiontext_syntax_highlighter.js","webpack://@ayushn21/actiontext-syntax-highlighter/./app/frontend/action_text_syntax_highlighter/index.js"],"names":["___CSS_LOADER_EXPORT___","push","module","id","exports","useSourceMap","list","toString","this","map","item","content","sourceMap","base64","data","cssMapping","btoa","sourceMapping","unescape","encodeURIComponent","JSON","stringify","concat","sourceURLs","sources","source","sourceRoot","join","cssWithMappingToString","i","modules","mediaQuery","dedupe","alreadyImportedModules","length","_i","memo","getTarget","target","styleTarget","document","querySelector","window","HTMLIFrameElement","contentDocument","head","e","stylesInDom","getIndexByIdentifier","identifier","result","modulesToDom","options","idCountMap","identifiers","base","count","index","obj","css","media","references","updater","addStyle","insertStyleElement","style","createElement","attributes","nonce","Object","keys","forEach","key","setAttribute","insert","Error","appendChild","textStore","replaceText","replacement","filter","Boolean","applyToSingletonTag","remove","styleSheet","cssText","cssNode","createTextNode","childNodes","removeChild","insertBefore","applyToTag","removeAttribute","firstChild","singleton","singletonCounter","update","styleIndex","bind","parentNode","removeStyleElement","newObj","all","atob","lastIdentifiers","newList","prototype","call","newLastIdentifiers","_index","splice","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","__webpack_modules__","n","getter","__esModule","d","a","definition","o","defineProperty","enumerable","get","prop","hasOwnProperty","HighlightedCodeBlock","HTMLElement","closest","editor","setup","dataset","trixHighlightedCodeBlock","trixId","parseInt","parentElement","attachment","getDocument","getAttachmentById","type","configureEventListeners","sgid","getAttributes","syntaxHighlighter","saveCodeBlock","toJSON","render","setAttributes","outerHTML","event","selectLanguage","value","save","language","languageSelector","codeBlock","addEventListener","languageChanged","highlighted_code_block","innerHTML","getDefaultHeaders","TrixCodeBlockHighlighter","element","fetch","ActionTextSyntaxHighlighter","basePath","method","headers","then","response","json","insertCodeBlock","block","body","Trix","Attachment","insertAttachment","composition","editAttachment","editorToolbar","interceptToolbarCodeButton","watchCursor","editorToolbarCodeButton","addCodeBlock","preventDefault","stopImmediatePropagation","updateToolbarState","getPosition","position","getPieceAtPosition","classList","add","disableStylingInteraction","toolbarElement","customElements","define","highlightsCodeBlocks","start"],"mappings":"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","file":"index.js","sourcesContent":["// Imports\nimport ___CSS_LOADER_API_IMPORT___ from \"../../../../node_modules/css-loader/dist/runtime/api.js\";\nvar ___CSS_LOADER_EXPORT___ = ___CSS_LOADER_API_IMPORT___(true);\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, \"trix-editor pre[contenteditable] {\\n -webkit-user-select: text;\\n user-select: text;\\n}\\n\\ntrix-editor pre[contenteditable]::selection {\\n background: highlight;\\n color: highlightText;\\n}\\n\\ntrix-editor select[name='language'] {\\n margin: 0.4em 0;\\n font-size: 0.8em;\\n padding: 0.4em 0;\\n}\\n\\nhighlighted-code-block > pre {\\n text-align: left;\\n outline: none;\\n}\\n\\nhighlighted-code-block > select {\\n display: inline-block;\\n float: right;\\n}\\n\\nfigure[data-trix-mutable][data-trix-highlighted-code-block] pre {\\n box-shadow: 0 0 0 1px highlight;\\n}\\n\\nfigure[data-trix-highlighted-code-block] {\\n width: 100%;\\n}\\n\\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--text-tools > button,\\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--block-tools > button,\\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--file-tools > button {\\n pointer-events: none;\\n}\\n\", \"\",{\"version\":3,\"sources\":[\"webpack://app/frontend/action_text_syntax_highlighter/css/highlighted_code_block.css\"],\"names\":[],\"mappings\":\"AAAA;EACE,yBAAyB;EACzB,iBAAiB;AACnB;;AAEA;EACE,qBAAqB;EACrB,oBAAoB;AACtB;;AAEA;EACE,eAAe;EACf,gBAAgB;EAChB,gBAAgB;AAClB;;AAEA;EACE,gBAAgB;EAChB,aAAa;AACf;;AAEA;EACE,qBAAqB;EACrB,YAAY;AACd;;AAEA;EACE,+BAA+B;AACjC;;AAEA;EACE,WAAW;AACb;;AAEA;;;EAGE,oBAAoB;AACtB\",\"sourcesContent\":[\"trix-editor pre[contenteditable] {\\n -webkit-user-select: text;\\n user-select: text;\\n}\\n\\ntrix-editor pre[contenteditable]::selection {\\n background: highlight;\\n color: highlightText;\\n}\\n\\ntrix-editor select[name='language'] {\\n margin: 0.4em 0;\\n font-size: 0.8em;\\n padding: 0.4em 0;\\n}\\n\\nhighlighted-code-block > pre {\\n text-align: left;\\n outline: none;\\n}\\n\\nhighlighted-code-block > select {\\n display: inline-block;\\n float: right;\\n}\\n\\nfigure[data-trix-mutable][data-trix-highlighted-code-block] pre {\\n box-shadow: 0 0 0 1px highlight;\\n}\\n\\nfigure[data-trix-highlighted-code-block] {\\n width: 100%;\\n}\\n\\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--text-tools > button,\\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--block-tools > button,\\ntrix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--file-tools > button {\\n pointer-events: none;\\n}\\n\"],\"sourceRoot\":\"\"}]);\n// Exports\nexport default ___CSS_LOADER_EXPORT___;\n","\"use strict\";\n\n/*\n MIT License http://www.opensource.org/licenses/mit-license.php\n Author Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\n// eslint-disable-next-line func-names\nmodule.exports = function (useSourceMap) {\n var list = []; // return the list of modules as css string\n\n list.toString = function toString() {\n return this.map(function (item) {\n var content = cssWithMappingToString(item, useSourceMap);\n\n if (item[2]) {\n return \"@media \".concat(item[2], \" {\").concat(content, \"}\");\n }\n\n return content;\n }).join('');\n }; // import a list of modules into the list\n // eslint-disable-next-line func-names\n\n\n list.i = function (modules, mediaQuery, dedupe) {\n if (typeof modules === 'string') {\n // eslint-disable-next-line no-param-reassign\n modules = [[null, modules, '']];\n }\n\n var alreadyImportedModules = {};\n\n if (dedupe) {\n for (var i = 0; i < this.length; i++) {\n // eslint-disable-next-line prefer-destructuring\n var id = this[i][0];\n\n if (id != null) {\n alreadyImportedModules[id] = true;\n }\n }\n }\n\n for (var _i = 0; _i < modules.length; _i++) {\n var item = [].concat(modules[_i]);\n\n if (dedupe && alreadyImportedModules[item[0]]) {\n // eslint-disable-next-line no-continue\n continue;\n }\n\n if (mediaQuery) {\n if (!item[2]) {\n item[2] = mediaQuery;\n } else {\n item[2] = \"\".concat(mediaQuery, \" and \").concat(item[2]);\n }\n }\n\n list.push(item);\n }\n };\n\n return list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n var content = item[1] || ''; // eslint-disable-next-line prefer-destructuring\n\n var cssMapping = item[3];\n\n if (!cssMapping) {\n return content;\n }\n\n if (useSourceMap && typeof btoa === 'function') {\n var sourceMapping = toComment(cssMapping);\n var sourceURLs = cssMapping.sources.map(function (source) {\n return \"/*# sourceURL=\".concat(cssMapping.sourceRoot || '').concat(source, \" */\");\n });\n return [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n }\n\n return [content].join('\\n');\n} // Adapted from convert-source-map (MIT)\n\n\nfunction toComment(sourceMap) {\n // eslint-disable-next-line no-undef\n var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n var data = \"sourceMappingURL=data:application/json;charset=utf-8;base64,\".concat(base64);\n return \"/*# \".concat(data, \" */\");\n}","\"use strict\";\n\nvar isOldIE = function isOldIE() {\n var memo;\n return function memorize() {\n if (typeof memo === 'undefined') {\n // Test for IE <= 9 as proposed by Browserhacks\n // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805\n // Tests for existence of standard globals is to allow style-loader\n // to operate correctly into non-standard environments\n // @see https://github.com/webpack-contrib/style-loader/issues/177\n memo = Boolean(window && document && document.all && !window.atob);\n }\n\n return memo;\n };\n}();\n\nvar getTarget = function getTarget() {\n var memo = {};\n return function memorize(target) {\n if (typeof memo[target] === 'undefined') {\n var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself\n\n if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {\n try {\n // This will throw an exception if access to iframe is blocked\n // due to cross-origin restrictions\n styleTarget = styleTarget.contentDocument.head;\n } catch (e) {\n // istanbul ignore next\n styleTarget = null;\n }\n }\n\n memo[target] = styleTarget;\n }\n\n return memo[target];\n };\n}();\n\nvar stylesInDom = [];\n\nfunction getIndexByIdentifier(identifier) {\n var result = -1;\n\n for (var i = 0; i < stylesInDom.length; i++) {\n if (stylesInDom[i].identifier === identifier) {\n result = i;\n break;\n }\n }\n\n return result;\n}\n\nfunction modulesToDom(list, options) {\n var idCountMap = {};\n var identifiers = [];\n\n for (var i = 0; i < list.length; i++) {\n var item = list[i];\n var id = options.base ? item[0] + options.base : item[0];\n var count = idCountMap[id] || 0;\n var identifier = \"\".concat(id, \" \").concat(count);\n idCountMap[id] = count + 1;\n var index = getIndexByIdentifier(identifier);\n var obj = {\n css: item[1],\n media: item[2],\n sourceMap: item[3]\n };\n\n if (index !== -1) {\n stylesInDom[index].references++;\n stylesInDom[index].updater(obj);\n } else {\n stylesInDom.push({\n identifier: identifier,\n updater: addStyle(obj, options),\n references: 1\n });\n }\n\n identifiers.push(identifier);\n }\n\n return identifiers;\n}\n\nfunction insertStyleElement(options) {\n var style = document.createElement('style');\n var attributes = options.attributes || {};\n\n if (typeof attributes.nonce === 'undefined') {\n var nonce = typeof __webpack_nonce__ !== 'undefined' ? __webpack_nonce__ : null;\n\n if (nonce) {\n attributes.nonce = nonce;\n }\n }\n\n Object.keys(attributes).forEach(function (key) {\n style.setAttribute(key, attributes[key]);\n });\n\n if (typeof options.insert === 'function') {\n options.insert(style);\n } else {\n var target = getTarget(options.insert || 'head');\n\n if (!target) {\n throw new Error(\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\");\n }\n\n target.appendChild(style);\n }\n\n return style;\n}\n\nfunction removeStyleElement(style) {\n // istanbul ignore if\n if (style.parentNode === null) {\n return false;\n }\n\n style.parentNode.removeChild(style);\n}\n/* istanbul ignore next */\n\n\nvar replaceText = function replaceText() {\n var textStore = [];\n return function replace(index, replacement) {\n textStore[index] = replacement;\n return textStore.filter(Boolean).join('\\n');\n };\n}();\n\nfunction applyToSingletonTag(style, index, remove, obj) {\n var css = remove ? '' : obj.media ? \"@media \".concat(obj.media, \" {\").concat(obj.css, \"}\") : obj.css; // For old IE\n\n /* istanbul ignore if */\n\n if (style.styleSheet) {\n style.styleSheet.cssText = replaceText(index, css);\n } else {\n var cssNode = document.createTextNode(css);\n var childNodes = style.childNodes;\n\n if (childNodes[index]) {\n style.removeChild(childNodes[index]);\n }\n\n if (childNodes.length) {\n style.insertBefore(cssNode, childNodes[index]);\n } else {\n style.appendChild(cssNode);\n }\n }\n}\n\nfunction applyToTag(style, options, obj) {\n var css = obj.css;\n var media = obj.media;\n var sourceMap = obj.sourceMap;\n\n if (media) {\n style.setAttribute('media', media);\n } else {\n style.removeAttribute('media');\n }\n\n if (sourceMap && typeof btoa !== 'undefined') {\n css += \"\\n/*# sourceMappingURL=data:application/json;base64,\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), \" */\");\n } // For old IE\n\n /* istanbul ignore if */\n\n\n if (style.styleSheet) {\n style.styleSheet.cssText = css;\n } else {\n while (style.firstChild) {\n style.removeChild(style.firstChild);\n }\n\n style.appendChild(document.createTextNode(css));\n }\n}\n\nvar singleton = null;\nvar singletonCounter = 0;\n\nfunction addStyle(obj, options) {\n var style;\n var update;\n var remove;\n\n if (options.singleton) {\n var styleIndex = singletonCounter++;\n style = singleton || (singleton = insertStyleElement(options));\n update = applyToSingletonTag.bind(null, style, styleIndex, false);\n remove = applyToSingletonTag.bind(null, style, styleIndex, true);\n } else {\n style = insertStyleElement(options);\n update = applyToTag.bind(null, style, options);\n\n remove = function remove() {\n removeStyleElement(style);\n };\n }\n\n update(obj);\n return function updateStyle(newObj) {\n if (newObj) {\n if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) {\n return;\n }\n\n update(obj = newObj);\n } else {\n remove();\n }\n };\n}\n\nmodule.exports = function (list, options) {\n options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>\n // tags it will allow on a page\n\n if (!options.singleton && typeof options.singleton !== 'boolean') {\n options.singleton = isOldIE();\n }\n\n list = list || [];\n var lastIdentifiers = modulesToDom(list, options);\n return function update(newList) {\n newList = newList || [];\n\n if (Object.prototype.toString.call(newList) !== '[object Array]') {\n return;\n }\n\n for (var i = 0; i < lastIdentifiers.length; i++) {\n var identifier = lastIdentifiers[i];\n var index = getIndexByIdentifier(identifier);\n stylesInDom[index].references--;\n }\n\n var newLastIdentifiers = modulesToDom(newList, options);\n\n for (var _i = 0; _i < lastIdentifiers.length; _i++) {\n var _identifier = lastIdentifiers[_i];\n\n var _index = getIndexByIdentifier(_identifier);\n\n if (stylesInDom[_index].references === 0) {\n stylesInDom[_index].updater();\n\n stylesInDom.splice(_index, 1);\n }\n }\n\n lastIdentifiers = newLastIdentifiers;\n };\n};","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\tid: moduleId,\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","export class HighlightedCodeBlock extends HTMLElement {\n connectedCallback() {\n if (this.closest(\"trix-editor\")?.editor) {\n this.setup()\n }\n }\n\n setup() {\n this.editor = this.closest(\"trix-editor\").editor\n this.closest(\"figure\").dataset.trixHighlightedCodeBlock = true\n\n this.trixId = parseInt(this.parentElement.dataset.trixId)\n this.attachment = this.editor.getDocument().getAttachmentById(this.trixId)\n this.attachment.type = \"HighlightedCodeBlock\"\n\n this.configureEventListeners()\n }\n\n save() {\n let sgid = this.attachment.getAttributes().sgid\n this.editor.syntaxHighlighter.saveCodeBlock(this.toJSON(), sgid)\n this.render()\n }\n\n render() {\n this.attachment.setAttributes({\n \"content\": this.outerHTML\n })\n }\n\n languageChanged(event) {\n this.selectLanguage(event.target.value)\n this.save()\n }\n\n selectLanguage(language) {\n this.languageSelector\n .querySelector(\"option[selected]\")\n .removeAttribute(\"selected\")\n\n this.languageSelector\n .querySelector(`option[value='${language}']`)\n .setAttribute(\"selected\", \"\")\n }\n\n configureEventListeners() {\n this.codeBlock\n .addEventListener(\"blur\", this.save.bind(this))\n this.languageSelector\n .addEventListener(\"change\", this.languageChanged.bind(this))\n }\n\n toJSON() {\n return JSON.stringify({\n highlighted_code_block: {\n content: this.codeBlock.innerHTML,\n language: this.languageSelector.value\n }\n })\n }\n\n get codeBlock() {\n return this.querySelector(\"pre\")\n }\n\n get languageSelector() {\n return this.querySelector(\"select\")\n }\n}","export function getDefaultHeaders() {\n return {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\",\n \"X-CSRF-Token\": getCsrfToken()\n }\n}\n\nexport function getCsrfToken() {\n return document.head.querySelector(\"meta[name='csrf-token']\").content\n}","import { ActionTextSyntaxHighlighter } from \"./actiontext_syntax_highlighter\"\nimport { getDefaultHeaders } from \"./helpers\"\n\nexport class TrixCodeBlockHighlighter {\n constructor(element) {\n this.element = element\n this.editor = element.editor\n\n this.setup()\n }\n\n addCodeBlock(event) {\n fetch(ActionTextSyntaxHighlighter.basePath, {\n method: \"POST\",\n headers: getDefaultHeaders()\n })\n .then(response => response.json())\n .then(result => this.insertCodeBlock(result))\n }\n\n saveCodeBlock(block, sgid) {\n fetch(`${ActionTextSyntaxHighlighter.basePath}/${sgid}`, {\n method: \"PATCH\",\n headers: getDefaultHeaders(),\n body: block\n })\n }\n\n insertCodeBlock(block) {\n let attachment = new Trix.Attachment(block)\n this.editor.insertAttachment(attachment)\n this.editor.composition.editAttachment(attachment)\n }\n\n // Private\n\n setup() {\n this.editorToolbar.setAttribute(\"data-highlights-code-blocks\", \"\")\n this.editor.syntaxHighlighter = this\n\n this.interceptToolbarCodeButton()\n this.watchCursor()\n\n // Setup the first highlighted code block which triggers a re-render\n // and sets them all up via the connectedCallback\n this.element.querySelector(\"highlighted-code-block\")?.setup()\n }\n\n interceptToolbarCodeButton() {\n this.editorToolbarCodeButton.addEventListener('mousedown', (event) => {\n this.addCodeBlock()\n event.preventDefault()\n event.stopImmediatePropagation()\n })\n }\n\n watchCursor() {\n this.element.addEventListener('trix-selection-change', event => {\n this.updateToolbarState(this.editor.getPosition())\n })\n }\n\n updateToolbarState(position) {\n let attachmentType = this.editor.getDocument().getPieceAtPosition(position).attachment?.type\n if (attachmentType == \"HighlightedCodeBlock\") {\n this.editorToolbarCodeButton.classList.add(\"trix-active\")\n this.editorToolbar.dataset.disableStylingInteraction = true\n } else {\n this.editorToolbarCodeButton.classList.remove(\"trix-active\")\n this.editorToolbar.removeAttribute(\"data-disable-styling-interaction\")\n }\n }\n\n get editorToolbar() {\n return this.element.toolbarElement\n }\n\n get editorToolbarCodeButton() {\n return this.editorToolbar.querySelector(\"[data-trix-attribute='code']\")\n }\n}","import api from \"!../../../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js\";\n import content from \"!!../../../../node_modules/css-loader/dist/cjs.js!./highlighted_code_block.css\";\n\nvar options = {};\n\noptions.insert = \"head\";\noptions.singleton = false;\n\nvar update = api(content, options);\n\n\n\nexport default content.locals || {};","import { HighlightedCodeBlock } from \"./highlighted_code_block\"\nimport { TrixCodeBlockHighlighter } from \"./trix_code_block_highlighter\"\nimport css from \"../css/highlighted_code_block.css\"\n\nexport class ActionTextSyntaxHighlighter {\n static basePath = \"/rails/action_text/highlighted_code_blocks\"\n\n static start() {\n customElements.define('highlighted-code-block', HighlightedCodeBlock)\n\n document.addEventListener(\"trix-initialize\", (event) => {\n if (event.target.dataset.highlightsCodeBlocks) {\n new TrixCodeBlockHighlighter(event.target)\n }\n })\n }\n}\n","import { ActionTextSyntaxHighlighter } from \"./javascript/actiontext_syntax_highlighter\"\n\nActionTextSyntaxHighlighter.start()"],"sourceRoot":""}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionTextSyntaxHighlighter
|
4
|
+
class HighlightedCodeBlocksController < ApplicationController
|
5
|
+
def create
|
6
|
+
@code_block = HighlightedCodeBlock.create
|
7
|
+
render json: {
|
8
|
+
sgid: @code_block.attachable_sgid,
|
9
|
+
contentType: "text/html",
|
10
|
+
content: render_to_string(
|
11
|
+
partial: @code_block.to_trix_content_attachment_partial_path,
|
12
|
+
locals: { highlighted_code_block: @code_block },
|
13
|
+
formats: [:html]
|
14
|
+
)
|
15
|
+
}, status: :created
|
16
|
+
end
|
17
|
+
|
18
|
+
def update
|
19
|
+
@code_block = ActionText::Attachable.from_attachable_sgid params[:id]
|
20
|
+
@code_block.update(code_block_params)
|
21
|
+
head :no_content
|
22
|
+
rescue ActiveRecord::RecordNotFound
|
23
|
+
head :not_found
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def code_block_params
|
28
|
+
params.require(:highlighted_code_block).permit(:content, :language)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
trix-editor pre[contenteditable] {
|
2
|
+
-webkit-user-select: text;
|
3
|
+
user-select: text;
|
4
|
+
}
|
5
|
+
|
6
|
+
trix-editor pre[contenteditable]::selection {
|
7
|
+
background: highlight;
|
8
|
+
color: highlightText;
|
9
|
+
}
|
10
|
+
|
11
|
+
trix-editor select[name='language'] {
|
12
|
+
margin: 0.4em 0;
|
13
|
+
font-size: 0.8em;
|
14
|
+
padding: 0.4em 0;
|
15
|
+
}
|
16
|
+
|
17
|
+
highlighted-code-block > pre {
|
18
|
+
text-align: left;
|
19
|
+
outline: none;
|
20
|
+
}
|
21
|
+
|
22
|
+
highlighted-code-block > select {
|
23
|
+
display: inline-block;
|
24
|
+
float: right;
|
25
|
+
}
|
26
|
+
|
27
|
+
figure[data-trix-mutable][data-trix-highlighted-code-block] pre {
|
28
|
+
box-shadow: 0 0 0 1px highlight;
|
29
|
+
}
|
30
|
+
|
31
|
+
figure[data-trix-highlighted-code-block] {
|
32
|
+
width: 100%;
|
33
|
+
}
|
34
|
+
|
35
|
+
trix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--text-tools > button,
|
36
|
+
trix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--block-tools > button,
|
37
|
+
trix-toolbar[data-highlights-code-blocks][data-disable-styling-interaction] .trix-button-group--file-tools > button {
|
38
|
+
pointer-events: none;
|
39
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { HighlightedCodeBlock } from "./highlighted_code_block"
|
2
|
+
import { TrixCodeBlockHighlighter } from "./trix_code_block_highlighter"
|
3
|
+
import css from "../css/highlighted_code_block.css"
|
4
|
+
|
5
|
+
export class ActionTextSyntaxHighlighter {
|
6
|
+
static basePath = "/rails/action_text/highlighted_code_blocks"
|
7
|
+
|
8
|
+
static start() {
|
9
|
+
customElements.define('highlighted-code-block', HighlightedCodeBlock)
|
10
|
+
|
11
|
+
document.addEventListener("trix-initialize", (event) => {
|
12
|
+
if (event.target.dataset.highlightsCodeBlocks) {
|
13
|
+
new TrixCodeBlockHighlighter(event.target)
|
14
|
+
}
|
15
|
+
})
|
16
|
+
}
|
17
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
export function getDefaultHeaders() {
|
2
|
+
return {
|
3
|
+
"Content-Type": "application/json",
|
4
|
+
"Accept": "application/json",
|
5
|
+
"X-CSRF-Token": getCsrfToken()
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
9
|
+
export function getCsrfToken() {
|
10
|
+
return document.head.querySelector("meta[name='csrf-token']").content
|
11
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
export class HighlightedCodeBlock extends HTMLElement {
|
2
|
+
connectedCallback() {
|
3
|
+
if (this.closest("trix-editor")?.editor) {
|
4
|
+
this.setup()
|
5
|
+
}
|
6
|
+
}
|
7
|
+
|
8
|
+
setup() {
|
9
|
+
this.editor = this.closest("trix-editor").editor
|
10
|
+
this.closest("figure").dataset.trixHighlightedCodeBlock = true
|
11
|
+
|
12
|
+
this.trixId = parseInt(this.parentElement.dataset.trixId)
|
13
|
+
this.attachment = this.editor.getDocument().getAttachmentById(this.trixId)
|
14
|
+
this.attachment.type = "HighlightedCodeBlock"
|
15
|
+
|
16
|
+
this.configureEventListeners()
|
17
|
+
}
|
18
|
+
|
19
|
+
save() {
|
20
|
+
let sgid = this.attachment.getAttributes().sgid
|
21
|
+
this.editor.syntaxHighlighter.saveCodeBlock(this.toJSON(), sgid)
|
22
|
+
this.render()
|
23
|
+
}
|
24
|
+
|
25
|
+
render() {
|
26
|
+
this.attachment.setAttributes({
|
27
|
+
"content": this.outerHTML
|
28
|
+
})
|
29
|
+
}
|
30
|
+
|
31
|
+
languageChanged(event) {
|
32
|
+
this.selectLanguage(event.target.value)
|
33
|
+
this.save()
|
34
|
+
}
|
35
|
+
|
36
|
+
selectLanguage(language) {
|
37
|
+
this.languageSelector
|
38
|
+
.querySelector("option[selected]")
|
39
|
+
.removeAttribute("selected")
|
40
|
+
|
41
|
+
this.languageSelector
|
42
|
+
.querySelector(`option[value='${language}']`)
|
43
|
+
.setAttribute("selected", "")
|
44
|
+
}
|
45
|
+
|
46
|
+
configureEventListeners() {
|
47
|
+
this.codeBlock
|
48
|
+
.addEventListener("blur", this.save.bind(this))
|
49
|
+
this.languageSelector
|
50
|
+
.addEventListener("change", this.languageChanged.bind(this))
|
51
|
+
}
|
52
|
+
|
53
|
+
toJSON() {
|
54
|
+
return JSON.stringify({
|
55
|
+
highlighted_code_block: {
|
56
|
+
content: this.codeBlock.innerHTML,
|
57
|
+
language: this.languageSelector.value
|
58
|
+
}
|
59
|
+
})
|
60
|
+
}
|
61
|
+
|
62
|
+
get codeBlock() {
|
63
|
+
return this.querySelector("pre")
|
64
|
+
}
|
65
|
+
|
66
|
+
get languageSelector() {
|
67
|
+
return this.querySelector("select")
|
68
|
+
}
|
69
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { ActionTextSyntaxHighlighter } from "./actiontext_syntax_highlighter"
|
2
|
+
import { getDefaultHeaders } from "./helpers"
|
3
|
+
|
4
|
+
export class TrixCodeBlockHighlighter {
|
5
|
+
constructor(element) {
|
6
|
+
this.element = element
|
7
|
+
this.editor = element.editor
|
8
|
+
|
9
|
+
this.setup()
|
10
|
+
}
|
11
|
+
|
12
|
+
addCodeBlock(event) {
|
13
|
+
fetch(ActionTextSyntaxHighlighter.basePath, {
|
14
|
+
method: "POST",
|
15
|
+
headers: getDefaultHeaders()
|
16
|
+
})
|
17
|
+
.then(response => response.json())
|
18
|
+
.then(result => this.insertCodeBlock(result))
|
19
|
+
}
|
20
|
+
|
21
|
+
saveCodeBlock(block, sgid) {
|
22
|
+
fetch(`${ActionTextSyntaxHighlighter.basePath}/${sgid}`, {
|
23
|
+
method: "PATCH",
|
24
|
+
headers: getDefaultHeaders(),
|
25
|
+
body: block
|
26
|
+
})
|
27
|
+
}
|
28
|
+
|
29
|
+
insertCodeBlock(block) {
|
30
|
+
let attachment = new Trix.Attachment(block)
|
31
|
+
this.editor.insertAttachment(attachment)
|
32
|
+
this.editor.composition.editAttachment(attachment)
|
33
|
+
}
|
34
|
+
|
35
|
+
// Private
|
36
|
+
|
37
|
+
setup() {
|
38
|
+
this.editorToolbar.setAttribute("data-highlights-code-blocks", "")
|
39
|
+
this.editor.syntaxHighlighter = this
|
40
|
+
|
41
|
+
this.interceptToolbarCodeButton()
|
42
|
+
this.watchCursor()
|
43
|
+
|
44
|
+
// Setup the first highlighted code block which triggers a re-render
|
45
|
+
// and sets them all up via the connectedCallback
|
46
|
+
this.element.querySelector("highlighted-code-block")?.setup()
|
47
|
+
}
|
48
|
+
|
49
|
+
interceptToolbarCodeButton() {
|
50
|
+
this.editorToolbarCodeButton.addEventListener('mousedown', (event) => {
|
51
|
+
this.addCodeBlock()
|
52
|
+
event.preventDefault()
|
53
|
+
event.stopImmediatePropagation()
|
54
|
+
})
|
55
|
+
}
|
56
|
+
|
57
|
+
watchCursor() {
|
58
|
+
this.element.addEventListener('trix-selection-change', event => {
|
59
|
+
this.updateToolbarState(this.editor.getPosition())
|
60
|
+
})
|
61
|
+
}
|
62
|
+
|
63
|
+
updateToolbarState(position) {
|
64
|
+
let attachmentType = this.editor.getDocument().getPieceAtPosition(position).attachment?.type
|
65
|
+
if (attachmentType == "HighlightedCodeBlock") {
|
66
|
+
this.editorToolbarCodeButton.classList.add("trix-active")
|
67
|
+
this.editorToolbar.dataset.disableStylingInteraction = true
|
68
|
+
} else {
|
69
|
+
this.editorToolbarCodeButton.classList.remove("trix-active")
|
70
|
+
this.editorToolbar.removeAttribute("data-disable-styling-interaction")
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
get editorToolbar() {
|
75
|
+
return this.element.toolbarElement
|
76
|
+
}
|
77
|
+
|
78
|
+
get editorToolbarCodeButton() {
|
79
|
+
return this.editorToolbar.querySelector("[data-trix-attribute='code']")
|
80
|
+
}
|
81
|
+
}
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActionTextSyntaxHighlighter::HighlightedCodeBlock < ApplicationRecord
|
4
|
+
include ActionText::Attachable, SupportedLanguages
|
5
|
+
|
6
|
+
self.table_name = "action_text_highlighted_code_blocks"
|
7
|
+
|
8
|
+
belongs_to :rich_text, class_name: "ActionText::RichText", optional: true
|
9
|
+
|
10
|
+
before_save :process_content
|
11
|
+
|
12
|
+
scope :deleted, -> { where(rich_text_id: nil).and(where(updated_at: ..1.day.ago)) }
|
13
|
+
|
14
|
+
def language
|
15
|
+
super || ""
|
16
|
+
end
|
17
|
+
|
18
|
+
def highlighted_content
|
19
|
+
return content unless lexer.present?
|
20
|
+
|
21
|
+
highlighter.format(lexer.lex(content)).html_safe
|
22
|
+
end
|
23
|
+
|
24
|
+
def content_type
|
25
|
+
"text/html"
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_trix_content_attachment_partial_path
|
29
|
+
"action_text_syntax_highlighter/highlighted_code_blocks/editor"
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def process_content
|
34
|
+
self.content&.gsub!(/<br>/, "\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def highlighter
|
38
|
+
@highlighter ||= Rouge::Formatters::HTML.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def lexer
|
42
|
+
@lexer ||= begin
|
43
|
+
if language.present?
|
44
|
+
Rouge::Lexer.find(language.downcase).new
|
45
|
+
else
|
46
|
+
Rouge::Lexer.guesses(source: content).first&.new
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ActionTextSyntaxHighlighter::HighlightedCodeBlock::SupportedLanguages
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class_methods do
|
5
|
+
def supported_languages
|
6
|
+
@supported_languages ||= Rouge::Lexer.all.map do |lexer|
|
7
|
+
lexer.to_s.split("::").last
|
8
|
+
end.sort.unshift("")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<highlighted-code-block>
|
2
|
+
<%= select_tag :language,
|
3
|
+
options_for_select(
|
4
|
+
ActionTextSyntaxHighlighter::HighlightedCodeBlock.supported_languages,
|
5
|
+
highlighted_code_block.language
|
6
|
+
) %>
|
7
|
+
<pre contenteditable=""><%= highlighted_code_block.content %></pre>
|
8
|
+
</highlighted-code-block>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
Rails.application.routes.draw do
|
2
|
+
scope ActionTextSyntaxHighlighter.routes_prefix do
|
3
|
+
post "/highlighted_code_blocks", to: "action_text_syntax_highlighter/highlighted_code_blocks#create"
|
4
|
+
patch "/highlighted_code_blocks/:id", to: "action_text_syntax_highlighter/highlighted_code_blocks#update", as: "highlighted_code_block"
|
5
|
+
end
|
6
|
+
end if ActionTextSyntaxHighlighter.draw_routes
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateActionTextHighlightedCodeBlocks < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
create_table :action_text_highlighted_code_blocks do |t|
|
4
|
+
t.text :content, size: :medium
|
5
|
+
t.string :language
|
6
|
+
t.references :rich_text, foreign_key: { to_table: :action_text_rich_texts, on_delete: :cascade }
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/rails"
|
5
|
+
require "nokogiri"
|
6
|
+
require "rouge"
|
7
|
+
|
8
|
+
module ActionTextSyntaxHighlighter
|
9
|
+
extend ActiveSupport::Autoload
|
10
|
+
|
11
|
+
autoload :HasHighlightedCodeBlocks
|
12
|
+
autoload :PreTagsToHighlightedCodeBlocksConversion
|
13
|
+
|
14
|
+
mattr_accessor :draw_routes, default: true
|
15
|
+
mattr_accessor :routes_prefix, default: "/rails/action_text"
|
16
|
+
mattr_accessor :default_theme, default: :github
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_text_syntax_highlighter"
|
4
|
+
|
5
|
+
module ActionTextSyntaxHighlighter
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
isolate_namespace ActionTextSyntaxHighlighter
|
8
|
+
|
9
|
+
config.action_text_syntax_highlighter = ActiveSupport::OrderedOptions.new
|
10
|
+
|
11
|
+
initializer "action_text_syntax_highlighter.allow_theme_attribute" do
|
12
|
+
Rails.application.config.to_prepare do
|
13
|
+
ActionText::ContentHelper.allowed_attributes << "data-theme"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer "action_text_syntax_highlighter.configs" do
|
18
|
+
config.after_initialize do |app|
|
19
|
+
ActionTextSyntaxHighlighter.draw_routes = app.config.action_text_syntax_highlighter.draw_routes != false
|
20
|
+
ActionTextSyntaxHighlighter.routes_prefix = app.config.action_text_syntax_highlighter.routes_prefix || "/rails/action_text"
|
21
|
+
ActionTextSyntaxHighlighter.default_theme = app.config.action_text_syntax_highlighter.default_theme || :github
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
initializer "action_text_syntax_highlighter.rich_text_includes" do
|
26
|
+
ActiveSupport.on_load(:action_text_rich_text) do
|
27
|
+
include ActionTextSyntaxHighlighter::HasHighlightedCodeBlocks
|
28
|
+
include ActionTextSyntaxHighlighter::PreTagsToHighlightedCodeBlocksConversion
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionTextSyntaxHighlighter
|
4
|
+
module HasHighlightedCodeBlocks
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_many :highlighted_code_blocks, class_name: "ActionTextSyntaxHighlighter::HighlightedCodeBlock"
|
9
|
+
before_save do
|
10
|
+
self.highlighted_code_blocks = body.attachables.grep(ActionTextSyntaxHighlighter::HighlightedCodeBlock).uniq if body.present?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionTextSyntaxHighlighter
|
4
|
+
module PreTagsToHighlightedCodeBlocksConversion
|
5
|
+
def convert_pre_tags_to_highlighted_code_blocks
|
6
|
+
@document = Nokogiri::HTML::DocumentFragment.parse(body.to_html)
|
7
|
+
@document.css("pre").each do |node|
|
8
|
+
code_block_content = sanitizer.sanitize node.inner_html
|
9
|
+
code_block = ActionTextSyntaxHighlighter::HighlightedCodeBlock.create(content: code_block_content)
|
10
|
+
node.swap attachment_node_for(code_block)
|
11
|
+
end
|
12
|
+
|
13
|
+
update(body: @document.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def attachment_node_for(code_block)
|
18
|
+
attachment_node = Nokogiri::XML::Node.new(attachment_tag_name, @document)
|
19
|
+
attachment_node[:"content-type"] = "text/html"
|
20
|
+
attachment_node[:sgid] = code_block.attachable_sgid
|
21
|
+
attachment_node
|
22
|
+
end
|
23
|
+
|
24
|
+
def attachment_tag_name
|
25
|
+
ActionText::Attachment.try(:tag_name) || ActionText::Attachment::TAG_NAME
|
26
|
+
end
|
27
|
+
|
28
|
+
def sanitizer
|
29
|
+
@sanitizer ||= Rails::Html::FullSanitizer.new
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :action_text_syntax_highlighter do
|
4
|
+
task :install do
|
5
|
+
Rake::Task["action_text_syntax_highlighter:install:migrations"].execute
|
6
|
+
puts "Install yarn package..."
|
7
|
+
# `yarn add @ayushn21/action_text_syntax_highlighter`
|
8
|
+
|
9
|
+
puts "All done! Just add the following lines to your JavaScript application:"
|
10
|
+
puts ""
|
11
|
+
puts <<~JAVASCRIPT
|
12
|
+
import { ActionTextSyntaxHighlighter } from "@ayushn21/actiontext-syntax-highlighter"
|
13
|
+
ActionTextSyntaxHighlighter.start()
|
14
|
+
JAVASCRIPT
|
15
|
+
|
16
|
+
puts ""
|
17
|
+
end
|
18
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: actiontext-syntax-highlighter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ayush Newatia
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-01 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: '6.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rouge
|
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
|
+
description:
|
42
|
+
email:
|
43
|
+
- ayush@hey.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE.md
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- app/assets/javascripts/action_text_syntax_highlighter.js
|
52
|
+
- app/assets/javascripts/action_text_syntax_highlighter.js.map
|
53
|
+
- app/controllers/action_text_syntax_highlighter/highlighted_code_blocks_controller.rb
|
54
|
+
- app/frontend/action_text_syntax_highlighter/css/highlighted_code_block.css
|
55
|
+
- app/frontend/action_text_syntax_highlighter/index.js
|
56
|
+
- app/frontend/action_text_syntax_highlighter/javascript/actiontext_syntax_highlighter.js
|
57
|
+
- app/frontend/action_text_syntax_highlighter/javascript/helpers.js
|
58
|
+
- app/frontend/action_text_syntax_highlighter/javascript/highlighted_code_block.js
|
59
|
+
- app/frontend/action_text_syntax_highlighter/javascript/trix_code_block_highlighter.js
|
60
|
+
- app/jobs/action_text_syntax_highlighter/purge_deleted_highlighted_code_blocks_job.rb
|
61
|
+
- app/models/action_text_syntax_highlighter/highlighted_code_block.rb
|
62
|
+
- app/models/action_text_syntax_highlighter/highlighted_code_block/supported_languages.rb
|
63
|
+
- app/views/action_text_syntax_highlighter/highlighted_code_blocks/_editor.html.erb
|
64
|
+
- app/views/action_text_syntax_highlighter/highlighted_code_blocks/_highlighted_code_block.html.erb
|
65
|
+
- config/routes.rb
|
66
|
+
- db/migrate/20210531195232_create_action_text_highlighted_code_blocks.rb
|
67
|
+
- lib/action_text_syntax_highlighter.rb
|
68
|
+
- lib/action_text_syntax_highlighter/engine.rb
|
69
|
+
- lib/action_text_syntax_highlighter/has_highlighted_code_blocks.rb
|
70
|
+
- lib/action_text_syntax_highlighter/pre_tags_to_highlighted_code_blocks_conversion.rb
|
71
|
+
- lib/action_text_syntax_highlighter/version.rb
|
72
|
+
- lib/tasks/action_text_syntax_highlighter_tasks.rake
|
73
|
+
homepage: https://github.com/ayushn21/actiontext-syntax-highlighter
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
homepage_uri: https://github.com/ayushn21/actiontext-syntax-highlighter
|
78
|
+
source_code_uri: https://github.com/ayushn21/actiontext-syntax-highlighter
|
79
|
+
changelog_uri: https://github.com/ayushn21/actiontext-syntax-highlighter/blob/main/CHANGELOG.md
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubygems_version: 3.1.4
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: Extends ActionText to support highlighted code blocks
|
99
|
+
test_files: []
|