trix-genius 0.1.1 → 0.1.2
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 +4 -4
- data/lib/generators/trix_genius/install/install_generator.rb +8 -13
- data/lib/generators/trix_genius/install/templates/trix_genius_controller.js +66 -13
- data/lib/generators/trix_genius/install/templates/trix_genius_controller.rb +93 -8
- data/lib/trix_genius/flexible_injector.rb +44 -0
- data/spec/tmp/dummy_app/app/controllers/trix_genius_controller.rb +93 -8
- data/spec/tmp/dummy_app/app/javascript/controllers/trix_genius_controller.js +66 -13
- data/spec/tmp/dummy_app/config/routes.rb +1 -0
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a05bf7cdc1b97afa037bf20356cebbabb06a89ab11f4770fc375a062aa6cdf35
|
4
|
+
data.tar.gz: 4987bc9f243bb514f969abeca9daea25a927e87a4f26a2c89f1acba2493a9f99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bab76153436f4713d8f52ff89ee982aeab277c51dfba671c587a3ee4496f3e2d57879492ac1aac49bb0e0241f19ac84b0e97c98b590281f29ac0f237688a85f8
|
7
|
+
data.tar.gz: 9ad9580d30792a4a3bd6c04f14626e23094bcd9d2300a021e22449d2cfb34eeb721c8cce304a8d24b93ca6b1b20bc7a35e7122cd589755ae0df233b1b7745350
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rails/generators"
|
2
|
+
require "trix_genius/flexible_injector"
|
2
3
|
|
3
4
|
module TrixGenius
|
4
5
|
module Generators
|
@@ -12,28 +13,21 @@ module TrixGenius
|
|
12
13
|
def add_import_to_application_js
|
13
14
|
js_application_path = "app/javascript/application.js"
|
14
15
|
js_application_path = File.join(destination_root, js_application_path)
|
15
|
-
|
16
|
-
if File.exist?(js_application_path)
|
17
|
-
application_file = File.read(js_application_path)
|
18
|
-
|
19
|
-
inject_into_file js_application_path, "\n" + 'import "trix"' + "\n", after: 'import "controllers"'
|
20
|
-
inject_into_file js_application_path, "\n" + 'import "@rails/actiontext"' + ("\n" * 2), after: 'import "trix"'
|
21
|
-
else
|
16
|
+
unless File.exist?(js_application_path)
|
22
17
|
puts javascript_application_msg
|
23
18
|
say_status("error", "Could not find #{js_application_path}", :red)
|
24
19
|
end
|
25
20
|
|
26
21
|
js_application_controller_path = "app/javascript/controllers/application.js"
|
27
22
|
js_application_controller_path = File.join(destination_root, js_application_controller_path)
|
28
|
-
|
29
|
-
if File.exist?(js_application_controller_path)
|
30
|
-
application_controller_file = File.read(js_application_controller_path)
|
31
|
-
inject_into_file js_application_controller_path, "\n" + 'import TrixController from "controllers/trix_genius_controller"' + "\n", after: 'import { Application } from "@hotwired/stimulus"'
|
32
|
-
inject_into_file js_application_controller_path, "\n" + 'application.register("trix", TrixController)' + ("\n" * 2), before: 'export { application }'
|
33
|
-
else
|
23
|
+
unless File.exist?(js_application_controller_path)
|
34
24
|
puts javascript_application_controller_msg
|
35
25
|
say_status("error", "Could not find #{js_application_controller_path}", :red)
|
36
26
|
end
|
27
|
+
|
28
|
+
gem_root = File.expand_path("../..", __dir__)
|
29
|
+
config_path = File.join("config", "setting_updates.yml")
|
30
|
+
FlexibleInjector.start([config_path, destination_root])
|
37
31
|
end
|
38
32
|
|
39
33
|
def create_stimulus_controller
|
@@ -44,6 +38,7 @@ module TrixGenius
|
|
44
38
|
route_code = ["",
|
45
39
|
" # TrixGenius: Auto-added route",
|
46
40
|
' post "/trix_genius/correct_spelling", to: "trix_genius#correct_spelling"',
|
41
|
+
' post "trix_genius/calculate_expression", to: "trix_genius#calculate_expression"',
|
47
42
|
""].join("\n")
|
48
43
|
|
49
44
|
inject_into_file File.join(destination_root, "config/routes.rb"), route_code, after: "Rails.application.routes.draw do\n"
|
@@ -4,26 +4,53 @@ export default class extends Controller {
|
|
4
4
|
// Use an arrow function to preserve `this` context
|
5
5
|
addEventListener("trix-initialize", (event) => {
|
6
6
|
const trixEditor = event.target;
|
7
|
+
const aiButtonCheckSpell = document.createElement("button");
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
aiButtonCheckSpell.setAttribute("type", "button");
|
10
|
+
aiButtonCheckSpell.setAttribute("tabindex", -1);
|
11
|
+
aiButtonCheckSpell.setAttribute("title", "Check Spelling");
|
12
|
+
aiButtonCheckSpell.classList.add("trix-button");
|
13
|
+
aiButtonCheckSpell.innerHTML = `
|
14
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
15
|
+
<!-- Main check icon -->
|
16
|
+
<path fill="#4CAF50" d="M21 7L9 19l-5.5-5.5 1.41-1.41L9 16.17 19.59 5.59 21 7z"/>
|
17
|
+
<path fill="#BBDEFB" d="M14 3v5h5M12 18v-2h6v2h-6zm0-4v-2h4v2h-4z"/>
|
18
|
+
<g transform="translate(18 2)">
|
19
|
+
<rect width="16" height="8" x="-16" y="0" fill="#FF9800" rx="1.5"/>
|
20
|
+
<text x="-8" y="4.5" font-size="6" font-family="Arial" fill="white" text-anchor="middle" dominant-baseline="middle">AI</text>
|
21
|
+
</g>
|
22
|
+
</svg>
|
23
|
+
`;
|
24
|
+
|
25
|
+
|
26
|
+
// Append the button to the toolbar
|
27
|
+
document.querySelector(".trix-button-group--text-tools").appendChild(aiButtonCheckSpell);
|
28
|
+
|
29
|
+
const aiButtonCalculateExpresions = document.createElement("button");
|
30
|
+
aiButtonCalculateExpresions.setAttribute("type", "button");
|
31
|
+
aiButtonCalculateExpresions.setAttribute("tabindex", -1);
|
32
|
+
aiButtonCalculateExpresions.setAttribute("title", "Calculate Expressions");
|
33
|
+
aiButtonCalculateExpresions.classList.add("trix-button");
|
34
|
+
aiButtonCalculateExpresions.innerHTML = `
|
35
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
36
|
+
<rect x="2" y="4" width="20" height="14" fill="#F5F5F5" stroke="#CCCCCC" stroke-width="1" rx="2" ry="2" />
|
37
|
+
<line x1="8" y1="11" x2="16" y2="11" stroke="#3498DB" stroke-width="2" />
|
38
|
+
<line x1="12" y1="7" x2="12" y2="15" stroke="#3498DB" stroke-width="2" />
|
39
|
+
</svg>
|
18
40
|
`;
|
19
41
|
|
20
42
|
// Append the button to the toolbar
|
21
|
-
document.querySelector(".trix-button-group--text-tools").appendChild(
|
43
|
+
document.querySelector(".trix-button-group--text-tools").appendChild(aiButtonCalculateExpresions);
|
22
44
|
|
23
45
|
// Attach the click event to the button
|
24
|
-
|
46
|
+
aiButtonCheckSpell.addEventListener("click", () => {
|
25
47
|
this.correctOrthography(trixEditor);
|
26
48
|
});
|
49
|
+
|
50
|
+
aiButtonCalculateExpresions.addEventListener("click", () => {
|
51
|
+
this.calculateExpression(trixEditor);
|
52
|
+
});
|
53
|
+
|
27
54
|
});
|
28
55
|
}
|
29
56
|
|
@@ -42,7 +69,6 @@ export default class extends Controller {
|
|
42
69
|
if (!response.ok) {
|
43
70
|
throw new Error("Network response was not ok");
|
44
71
|
}
|
45
|
-
|
46
72
|
const result = await response.json();
|
47
73
|
|
48
74
|
editor.loadHTML(result.corrected_text);
|
@@ -51,4 +77,31 @@ export default class extends Controller {
|
|
51
77
|
alert("An error occurred while correcting orthography.");
|
52
78
|
}
|
53
79
|
}
|
80
|
+
|
81
|
+
|
82
|
+
async calculateExpression(trixEditor) {
|
83
|
+
try {
|
84
|
+
const editor = trixEditor.editor;
|
85
|
+
const content = editor.getDocument().toString(); // Get the current content
|
86
|
+
|
87
|
+
// Send the content to the backend for correction
|
88
|
+
const response = await fetch("/trix_genius/calculate_expression", {
|
89
|
+
method: "POST",
|
90
|
+
headers: { "Content-Type": "application/json" },
|
91
|
+
body: JSON.stringify({ text: content }),
|
92
|
+
});
|
93
|
+
|
94
|
+
if (!response.ok) {
|
95
|
+
throw new Error("Network response was not ok");
|
96
|
+
}
|
97
|
+
|
98
|
+
const result = await response.json();
|
99
|
+
editor.loadHTML(result.calculus.replace(/\n/g, "<br>"));
|
100
|
+
|
101
|
+
} catch (error) {
|
102
|
+
console.error("Error Calculate Expressions:", error);
|
103
|
+
alert("An error occurred while calculate expression.");
|
104
|
+
}
|
105
|
+
}
|
54
106
|
}
|
107
|
+
|
@@ -3,23 +3,107 @@ require 'faraday'
|
|
3
3
|
class TrixGeniusController < ApplicationController
|
4
4
|
skip_before_action :verify_authenticity_token
|
5
5
|
|
6
|
-
def
|
6
|
+
def calculate_expression
|
7
7
|
text = params.require(:text)
|
8
|
+
calculus = call_ai_calculate_expression(text)
|
9
|
+
render json: { calculus: calculus }
|
10
|
+
rescue StandardError => e
|
11
|
+
Rails.logger.error("Calculate Expression Error: #{e.message}")
|
12
|
+
render json: { error: "An error occurred while CalculatingExpression." }, status: :unprocessable_entity
|
13
|
+
end
|
8
14
|
|
9
|
-
|
10
|
-
|
15
|
+
def correct_spelling
|
16
|
+
text = params.require(:text)
|
17
|
+
corrected_text = call_ai_check_spell(text)
|
11
18
|
render json: { corrected_text: corrected_text }
|
12
19
|
rescue StandardError => e
|
13
20
|
Rails.logger.error("Orthography Correction Error: #{e.message}")
|
14
21
|
render json: { error: "An error occurred while correcting orthography." }, status: :unprocessable_entity
|
15
22
|
end
|
16
23
|
|
24
|
+
|
25
|
+
|
17
26
|
private
|
18
27
|
|
19
|
-
def
|
28
|
+
def evaluate_expression(expr)
|
29
|
+
# Allow only numbers, operators, and parentheses
|
30
|
+
allowed_chars = Set.new('0123456789+-*/(). '.chars)
|
31
|
+
return nil unless expr.chars.all? { |c| allowed_chars.include?(c) }
|
32
|
+
|
33
|
+
# Convert integers to floats for proper division
|
34
|
+
sanitized = expr.gsub(/(\d+(?:\.\d+)?)/) { |m| m.include?('.') ? m : "#{m}.0" }
|
35
|
+
|
36
|
+
begin
|
37
|
+
result = eval(sanitized)
|
38
|
+
# Convert whole numbers to integers for cleaner output
|
39
|
+
result = result.to_i if result.is_a?(Float) && result == result.floor
|
40
|
+
result
|
41
|
+
rescue
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_text(text)
|
47
|
+
stack = []
|
48
|
+
expressions = []
|
49
|
+
|
50
|
+
# Find all top-level parentheses expressions
|
51
|
+
text.chars.each_with_index do |char, i|
|
52
|
+
if char == '('
|
53
|
+
stack.push(i)
|
54
|
+
elsif char == ')' && stack.any?
|
55
|
+
start_idx = stack.pop
|
56
|
+
if stack.empty? # Only track top-level parentheses
|
57
|
+
expressions << { start: start_idx, end: i, expr: text[start_idx..i] }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Process from last to first to preserve positions
|
63
|
+
expressions.reverse.each do |exp|
|
64
|
+
inner_expr = exp[:expr][1...-1] # Remove outer parentheses
|
65
|
+
result = evaluate_expression(inner_expr)
|
66
|
+
|
67
|
+
next unless result
|
68
|
+
|
69
|
+
replacement = "#{inner_expr}=#{result}"
|
70
|
+
text = text[0...exp[:start]] + replacement + text[exp[:end]+1..-1]
|
71
|
+
end
|
72
|
+
|
73
|
+
text
|
74
|
+
end
|
75
|
+
|
76
|
+
def call_ai_calculate_expression(text)
|
77
|
+
processed_text = process_text(text)
|
78
|
+
headers = {
|
79
|
+
'Content-Type' => 'application/json',
|
80
|
+
'Authorization' => "Bearer #{Rails.application.config.deepseek[:api_key]}"
|
81
|
+
}
|
82
|
+
|
83
|
+
body = {
|
84
|
+
model: "deepseek-chat",
|
85
|
+
messages: [{
|
86
|
+
role: "user",
|
87
|
+
content: "Format this text maintaining all original content but with calculated expressions: #{processed_text}"
|
88
|
+
}],
|
89
|
+
temperature: 0.7,
|
90
|
+
max_tokens: 500
|
91
|
+
}.to_json
|
92
|
+
|
93
|
+
response = Faraday.post(Rails.application.config.deepseek[:api_url], body, headers)
|
94
|
+
|
95
|
+
if response.success?
|
96
|
+
str = JSON.parse(response.body)['choices'][0]['message']['content'].split("---")[1].gsub("*", "")
|
97
|
+
return str
|
98
|
+
else
|
99
|
+
puts "Error: #{response.status} - #{response.body}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def call_ai_check_spell(text)
|
20
104
|
headers = {
|
21
105
|
'Content-Type' => 'application/json',
|
22
|
-
'Authorization' => "Bearer #{Rails.application.config.deepseek[:api_key}"
|
106
|
+
'Authorization' => "Bearer #{Rails.application.config.deepseek[:api_key]}"
|
23
107
|
}
|
24
108
|
|
25
109
|
body = {
|
@@ -29,12 +113,13 @@ class TrixGeniusController < ApplicationController
|
|
29
113
|
max_tokens: 500
|
30
114
|
}.to_json
|
31
115
|
|
32
|
-
response = Faraday.post(Rails.application.config.deepseek[:api_url]
|
116
|
+
response = Faraday.post(Rails.application.config.deepseek[:api_url], body, headers)
|
33
117
|
|
34
118
|
if response.success?
|
35
|
-
return JSON.parse(response.body)['choices'][
|
119
|
+
return JSON.parse(response.body)['choices'][1]['message']['content'].split('"')[1]
|
36
120
|
else
|
37
|
-
"Error: #{response.status} - #{response.body}"
|
121
|
+
puts "Error: #{response.status} - #{response.body}"
|
38
122
|
end
|
39
123
|
end
|
40
124
|
end
|
125
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'thor'
|
3
|
+
require 'thor/actions'
|
4
|
+
|
5
|
+
module TrixGenius
|
6
|
+
class FlexibleInjector < Thor::Group
|
7
|
+
include Thor::Actions
|
8
|
+
argument :file_name, type: :string
|
9
|
+
argument :base_path, type: :string
|
10
|
+
|
11
|
+
def self.source_root
|
12
|
+
Dir.pwd
|
13
|
+
end
|
14
|
+
|
15
|
+
def apply_from_config
|
16
|
+
injections = YAML.load_file(file_name)
|
17
|
+
|
18
|
+
injections.each do |injection|
|
19
|
+
file_path = injection['file']
|
20
|
+
content = "\n" + injection['content'] + "\n"
|
21
|
+
message = (injection['message'] || 'insert').to_sym
|
22
|
+
options = {}
|
23
|
+
options[:after] = injection['after'] if injection['after']
|
24
|
+
options[:before] = injection['before'] if injection['before']
|
25
|
+
|
26
|
+
complete_path = File.join(base_path, file_path)
|
27
|
+
|
28
|
+
case message
|
29
|
+
when :insert
|
30
|
+
say_status :insert, complete_path
|
31
|
+
inject_into_file(complete_path, content, options)
|
32
|
+
when :append
|
33
|
+
say_status :append, complete_path
|
34
|
+
inject_into_file(complete_path, content)
|
35
|
+
else
|
36
|
+
say_status :skip, "#{complete_path} — unknown message: #{message}", :yellow
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -3,23 +3,107 @@ require 'faraday'
|
|
3
3
|
class TrixGeniusController < ApplicationController
|
4
4
|
skip_before_action :verify_authenticity_token
|
5
5
|
|
6
|
-
def
|
6
|
+
def calculate_expression
|
7
7
|
text = params.require(:text)
|
8
|
+
calculus = call_ai_calculate_expression(text)
|
9
|
+
render json: { calculus: calculus }
|
10
|
+
rescue StandardError => e
|
11
|
+
Rails.logger.error("Calculate Expression Error: #{e.message}")
|
12
|
+
render json: { error: "An error occurred while CalculatingExpression." }, status: :unprocessable_entity
|
13
|
+
end
|
8
14
|
|
9
|
-
|
10
|
-
|
15
|
+
def correct_spelling
|
16
|
+
text = params.require(:text)
|
17
|
+
corrected_text = call_ai_check_spell(text)
|
11
18
|
render json: { corrected_text: corrected_text }
|
12
19
|
rescue StandardError => e
|
13
20
|
Rails.logger.error("Orthography Correction Error: #{e.message}")
|
14
21
|
render json: { error: "An error occurred while correcting orthography." }, status: :unprocessable_entity
|
15
22
|
end
|
16
23
|
|
24
|
+
|
25
|
+
|
17
26
|
private
|
18
27
|
|
19
|
-
def
|
28
|
+
def evaluate_expression(expr)
|
29
|
+
# Allow only numbers, operators, and parentheses
|
30
|
+
allowed_chars = Set.new('0123456789+-*/(). '.chars)
|
31
|
+
return nil unless expr.chars.all? { |c| allowed_chars.include?(c) }
|
32
|
+
|
33
|
+
# Convert integers to floats for proper division
|
34
|
+
sanitized = expr.gsub(/(\d+(?:\.\d+)?)/) { |m| m.include?('.') ? m : "#{m}.0" }
|
35
|
+
|
36
|
+
begin
|
37
|
+
result = eval(sanitized)
|
38
|
+
# Convert whole numbers to integers for cleaner output
|
39
|
+
result = result.to_i if result.is_a?(Float) && result == result.floor
|
40
|
+
result
|
41
|
+
rescue
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def process_text(text)
|
47
|
+
stack = []
|
48
|
+
expressions = []
|
49
|
+
|
50
|
+
# Find all top-level parentheses expressions
|
51
|
+
text.chars.each_with_index do |char, i|
|
52
|
+
if char == '('
|
53
|
+
stack.push(i)
|
54
|
+
elsif char == ')' && stack.any?
|
55
|
+
start_idx = stack.pop
|
56
|
+
if stack.empty? # Only track top-level parentheses
|
57
|
+
expressions << { start: start_idx, end: i, expr: text[start_idx..i] }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Process from last to first to preserve positions
|
63
|
+
expressions.reverse.each do |exp|
|
64
|
+
inner_expr = exp[:expr][1...-1] # Remove outer parentheses
|
65
|
+
result = evaluate_expression(inner_expr)
|
66
|
+
|
67
|
+
next unless result
|
68
|
+
|
69
|
+
replacement = "#{inner_expr}=#{result}"
|
70
|
+
text = text[0...exp[:start]] + replacement + text[exp[:end]+1..-1]
|
71
|
+
end
|
72
|
+
|
73
|
+
text
|
74
|
+
end
|
75
|
+
|
76
|
+
def call_ai_calculate_expression(text)
|
77
|
+
processed_text = process_text(text)
|
78
|
+
headers = {
|
79
|
+
'Content-Type' => 'application/json',
|
80
|
+
'Authorization' => "Bearer #{Rails.application.config.deepseek[:api_key]}"
|
81
|
+
}
|
82
|
+
|
83
|
+
body = {
|
84
|
+
model: "deepseek-chat",
|
85
|
+
messages: [{
|
86
|
+
role: "user",
|
87
|
+
content: "Format this text maintaining all original content but with calculated expressions: #{processed_text}"
|
88
|
+
}],
|
89
|
+
temperature: 0.7,
|
90
|
+
max_tokens: 500
|
91
|
+
}.to_json
|
92
|
+
|
93
|
+
response = Faraday.post(Rails.application.config.deepseek[:api_url], body, headers)
|
94
|
+
|
95
|
+
if response.success?
|
96
|
+
str = JSON.parse(response.body)['choices'][0]['message']['content'].split("---")[1].gsub("*", "")
|
97
|
+
return str
|
98
|
+
else
|
99
|
+
puts "Error: #{response.status} - #{response.body}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def call_ai_check_spell(text)
|
20
104
|
headers = {
|
21
105
|
'Content-Type' => 'application/json',
|
22
|
-
'Authorization' => "Bearer #{Rails.application.config.deepseek[:api_key}"
|
106
|
+
'Authorization' => "Bearer #{Rails.application.config.deepseek[:api_key]}"
|
23
107
|
}
|
24
108
|
|
25
109
|
body = {
|
@@ -29,12 +113,13 @@ class TrixGeniusController < ApplicationController
|
|
29
113
|
max_tokens: 500
|
30
114
|
}.to_json
|
31
115
|
|
32
|
-
response = Faraday.post(Rails.application.config.deepseek[:api_url]
|
116
|
+
response = Faraday.post(Rails.application.config.deepseek[:api_url], body, headers)
|
33
117
|
|
34
118
|
if response.success?
|
35
|
-
return JSON.parse(response.body)['choices'][
|
119
|
+
return JSON.parse(response.body)['choices'][1]['message']['content'].split('"')[1]
|
36
120
|
else
|
37
|
-
"Error: #{response.status} - #{response.body}"
|
121
|
+
puts "Error: #{response.status} - #{response.body}"
|
38
122
|
end
|
39
123
|
end
|
40
124
|
end
|
125
|
+
|
@@ -4,26 +4,53 @@ export default class extends Controller {
|
|
4
4
|
// Use an arrow function to preserve `this` context
|
5
5
|
addEventListener("trix-initialize", (event) => {
|
6
6
|
const trixEditor = event.target;
|
7
|
+
const aiButtonCheckSpell = document.createElement("button");
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
aiButtonCheckSpell.setAttribute("type", "button");
|
10
|
+
aiButtonCheckSpell.setAttribute("tabindex", -1);
|
11
|
+
aiButtonCheckSpell.setAttribute("title", "Check Spelling");
|
12
|
+
aiButtonCheckSpell.classList.add("trix-button");
|
13
|
+
aiButtonCheckSpell.innerHTML = `
|
14
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
15
|
+
<!-- Main check icon -->
|
16
|
+
<path fill="#4CAF50" d="M21 7L9 19l-5.5-5.5 1.41-1.41L9 16.17 19.59 5.59 21 7z"/>
|
17
|
+
<path fill="#BBDEFB" d="M14 3v5h5M12 18v-2h6v2h-6zm0-4v-2h4v2h-4z"/>
|
18
|
+
<g transform="translate(18 2)">
|
19
|
+
<rect width="16" height="8" x="-16" y="0" fill="#FF9800" rx="1.5"/>
|
20
|
+
<text x="-8" y="4.5" font-size="6" font-family="Arial" fill="white" text-anchor="middle" dominant-baseline="middle">AI</text>
|
21
|
+
</g>
|
22
|
+
</svg>
|
23
|
+
`;
|
24
|
+
|
25
|
+
|
26
|
+
// Append the button to the toolbar
|
27
|
+
document.querySelector(".trix-button-group--text-tools").appendChild(aiButtonCheckSpell);
|
28
|
+
|
29
|
+
const aiButtonCalculateExpresions = document.createElement("button");
|
30
|
+
aiButtonCalculateExpresions.setAttribute("type", "button");
|
31
|
+
aiButtonCalculateExpresions.setAttribute("tabindex", -1);
|
32
|
+
aiButtonCalculateExpresions.setAttribute("title", "Calculate Expressions");
|
33
|
+
aiButtonCalculateExpresions.classList.add("trix-button");
|
34
|
+
aiButtonCalculateExpresions.innerHTML = `
|
35
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
36
|
+
<rect x="2" y="4" width="20" height="14" fill="#F5F5F5" stroke="#CCCCCC" stroke-width="1" rx="2" ry="2" />
|
37
|
+
<line x1="8" y1="11" x2="16" y2="11" stroke="#3498DB" stroke-width="2" />
|
38
|
+
<line x1="12" y1="7" x2="12" y2="15" stroke="#3498DB" stroke-width="2" />
|
39
|
+
</svg>
|
18
40
|
`;
|
19
41
|
|
20
42
|
// Append the button to the toolbar
|
21
|
-
document.querySelector(".trix-button-group--text-tools").appendChild(
|
43
|
+
document.querySelector(".trix-button-group--text-tools").appendChild(aiButtonCalculateExpresions);
|
22
44
|
|
23
45
|
// Attach the click event to the button
|
24
|
-
|
46
|
+
aiButtonCheckSpell.addEventListener("click", () => {
|
25
47
|
this.correctOrthography(trixEditor);
|
26
48
|
});
|
49
|
+
|
50
|
+
aiButtonCalculateExpresions.addEventListener("click", () => {
|
51
|
+
this.calculateExpression(trixEditor);
|
52
|
+
});
|
53
|
+
|
27
54
|
});
|
28
55
|
}
|
29
56
|
|
@@ -42,7 +69,6 @@ export default class extends Controller {
|
|
42
69
|
if (!response.ok) {
|
43
70
|
throw new Error("Network response was not ok");
|
44
71
|
}
|
45
|
-
|
46
72
|
const result = await response.json();
|
47
73
|
|
48
74
|
editor.loadHTML(result.corrected_text);
|
@@ -51,4 +77,31 @@ export default class extends Controller {
|
|
51
77
|
alert("An error occurred while correcting orthography.");
|
52
78
|
}
|
53
79
|
}
|
80
|
+
|
81
|
+
|
82
|
+
async calculateExpression(trixEditor) {
|
83
|
+
try {
|
84
|
+
const editor = trixEditor.editor;
|
85
|
+
const content = editor.getDocument().toString(); // Get the current content
|
86
|
+
|
87
|
+
// Send the content to the backend for correction
|
88
|
+
const response = await fetch("/trix_genius/calculate_expression", {
|
89
|
+
method: "POST",
|
90
|
+
headers: { "Content-Type": "application/json" },
|
91
|
+
body: JSON.stringify({ text: content }),
|
92
|
+
});
|
93
|
+
|
94
|
+
if (!response.ok) {
|
95
|
+
throw new Error("Network response was not ok");
|
96
|
+
}
|
97
|
+
|
98
|
+
const result = await response.json();
|
99
|
+
editor.loadHTML(result.calculus.replace(/\n/g, "<br>"));
|
100
|
+
|
101
|
+
} catch (error) {
|
102
|
+
console.error("Error Calculate Expressions:", error);
|
103
|
+
alert("An error occurred while calculate expression.");
|
104
|
+
}
|
105
|
+
}
|
54
106
|
}
|
107
|
+
|
@@ -2,6 +2,7 @@ Rails.application.routes.draw do
|
|
2
2
|
|
3
3
|
# TrixGenius: Auto-added route
|
4
4
|
post "/trix_genius/correct_spelling", to: "trix_genius#correct_spelling"
|
5
|
+
post "trix_genius/calculate_expression", to: "trix_genius#calculate_expression"
|
5
6
|
resources :posts
|
6
7
|
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
7
8
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trix-genius
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Giménez Silva Germán Alberto https://rubystacknews.com/
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-04-
|
10
|
+
date: 2025-04-15 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: rails
|
@@ -71,6 +71,20 @@ dependencies:
|
|
71
71
|
- - "~>"
|
72
72
|
- !ruby/object:Gem::Version
|
73
73
|
version: '2.12'
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: yaml
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
type: :runtime
|
82
|
+
prerelease: false
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
74
88
|
- !ruby/object:Gem::Dependency
|
75
89
|
name: rspec
|
76
90
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,7 +114,7 @@ dependencies:
|
|
100
114
|
- !ruby/object:Gem::Version
|
101
115
|
version: '3.0'
|
102
116
|
description: Trix-Genius adds AI-powered buttons and other custom controls to Trix
|
103
|
-
editor using Stimulus.
|
117
|
+
editor using Stimulus. https://rubystacknews.com/
|
104
118
|
email: ggerman@gmail.com
|
105
119
|
executables: []
|
106
120
|
extensions: []
|
@@ -112,6 +126,7 @@ files:
|
|
112
126
|
- lib/generators/trix_genius/install/templates/trix_genius_controller.rb
|
113
127
|
- lib/trix-genius.rb
|
114
128
|
- lib/trix_genius/engine.rb
|
129
|
+
- lib/trix_genius/flexible_injector.rb
|
115
130
|
- spec/generators/trix_genius/install/install_generator_spec.rb
|
116
131
|
- spec/spec_helper.rb
|
117
132
|
- spec/tmp/dummy_app/app/controllers/trix_genius_controller.rb
|