active_canvas 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +318 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/active_canvas/editor/ai_panel.js +1607 -0
- data/app/assets/javascripts/active_canvas/editor/asset_manager.js +498 -0
- data/app/assets/javascripts/active_canvas/editor/blocks.js +1083 -0
- data/app/assets/javascripts/active_canvas/editor/code_panel.js +572 -0
- data/app/assets/javascripts/active_canvas/editor/component_toolbar.js +394 -0
- data/app/assets/javascripts/active_canvas/editor/panels.js +460 -0
- data/app/assets/javascripts/active_canvas/editor/utils.js +56 -0
- data/app/assets/javascripts/active_canvas/editor.js +295 -0
- data/app/assets/stylesheets/active_canvas/application.css +15 -0
- data/app/assets/stylesheets/active_canvas/editor.css +2929 -0
- data/app/controllers/active_canvas/admin/ai_controller.rb +181 -0
- data/app/controllers/active_canvas/admin/application_controller.rb +56 -0
- data/app/controllers/active_canvas/admin/media_controller.rb +61 -0
- data/app/controllers/active_canvas/admin/page_types_controller.rb +57 -0
- data/app/controllers/active_canvas/admin/page_versions_controller.rb +23 -0
- data/app/controllers/active_canvas/admin/pages_controller.rb +133 -0
- data/app/controllers/active_canvas/admin/partials_controller.rb +88 -0
- data/app/controllers/active_canvas/admin/settings_controller.rb +256 -0
- data/app/controllers/active_canvas/application_controller.rb +20 -0
- data/app/controllers/active_canvas/pages_controller.rb +18 -0
- data/app/controllers/concerns/active_canvas/current_user.rb +12 -0
- data/app/controllers/concerns/active_canvas/rate_limitable.rb +75 -0
- data/app/controllers/concerns/active_canvas/tailwind_compilation.rb +39 -0
- data/app/helpers/active_canvas/application_helper.rb +4 -0
- data/app/jobs/active_canvas/application_job.rb +4 -0
- data/app/jobs/active_canvas/compile_tailwind_job.rb +64 -0
- data/app/mailers/active_canvas/application_mailer.rb +6 -0
- data/app/models/active_canvas/ai_model.rb +136 -0
- data/app/models/active_canvas/application_record.rb +5 -0
- data/app/models/active_canvas/media.rb +141 -0
- data/app/models/active_canvas/page.rb +85 -0
- data/app/models/active_canvas/page_type.rb +22 -0
- data/app/models/active_canvas/page_version.rb +80 -0
- data/app/models/active_canvas/partial.rb +73 -0
- data/app/models/active_canvas/setting.rb +292 -0
- data/app/services/active_canvas/ai_configuration.rb +40 -0
- data/app/services/active_canvas/ai_models.rb +128 -0
- data/app/services/active_canvas/ai_service.rb +289 -0
- data/app/services/active_canvas/content_sanitizer.rb +112 -0
- data/app/services/active_canvas/tailwind_compiler.rb +156 -0
- data/app/views/active_canvas/admin/media/index.html.erb +401 -0
- data/app/views/active_canvas/admin/media/show.html.erb +297 -0
- data/app/views/active_canvas/admin/page_types/_form.html.erb +25 -0
- data/app/views/active_canvas/admin/page_types/edit.html.erb +13 -0
- data/app/views/active_canvas/admin/page_types/index.html.erb +29 -0
- data/app/views/active_canvas/admin/page_types/new.html.erb +9 -0
- data/app/views/active_canvas/admin/page_types/show.html.erb +18 -0
- data/app/views/active_canvas/admin/page_versions/show.html.erb +469 -0
- data/app/views/active_canvas/admin/pages/_form.html.erb +62 -0
- data/app/views/active_canvas/admin/pages/content.html.erb +139 -0
- data/app/views/active_canvas/admin/pages/edit.html.erb +335 -0
- data/app/views/active_canvas/admin/pages/editor.html.erb +710 -0
- data/app/views/active_canvas/admin/pages/index.html.erb +149 -0
- data/app/views/active_canvas/admin/pages/new.html.erb +19 -0
- data/app/views/active_canvas/admin/pages/show.html.erb +258 -0
- data/app/views/active_canvas/admin/pages/versions.html.erb +333 -0
- data/app/views/active_canvas/admin/partials/edit.html.erb +182 -0
- data/app/views/active_canvas/admin/partials/editor.html.erb +703 -0
- data/app/views/active_canvas/admin/partials/index.html.erb +131 -0
- data/app/views/active_canvas/admin/settings/show.html.erb +1864 -0
- data/app/views/active_canvas/pages/no_homepage.html.erb +45 -0
- data/app/views/active_canvas/pages/show.html.erb +113 -0
- data/app/views/layouts/active_canvas/admin/application.html.erb +960 -0
- data/app/views/layouts/active_canvas/admin/editor.html.erb +826 -0
- data/app/views/layouts/active_canvas/application.html.erb +55 -0
- data/config/routes.rb +48 -0
- data/db/migrate/20260202000001_create_active_canvas_tables.rb +113 -0
- data/db/migrate/20260202000002_create_active_canvas_ai_models.rb +26 -0
- data/lib/active_canvas/configuration.rb +232 -0
- data/lib/active_canvas/engine.rb +44 -0
- data/lib/active_canvas/version.rb +3 -0
- data/lib/active_canvas.rb +26 -0
- data/lib/generators/active_canvas/install/install_generator.rb +263 -0
- data/lib/generators/active_canvas/install/templates/initializer.rb.tt +163 -0
- data/lib/tasks/active_canvas_tasks.rake +69 -0
- metadata +150 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
module ActiveCanvas
|
|
2
|
+
module Generators
|
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
|
4
|
+
source_root File.expand_path("templates", __dir__)
|
|
5
|
+
|
|
6
|
+
desc "Interactive setup wizard for ActiveCanvas"
|
|
7
|
+
|
|
8
|
+
def welcome
|
|
9
|
+
say ""
|
|
10
|
+
say "=" * 60, :cyan
|
|
11
|
+
say " Welcome to ActiveCanvas Setup Wizard", :cyan
|
|
12
|
+
say "=" * 60, :cyan
|
|
13
|
+
say ""
|
|
14
|
+
say "This wizard will help you set up ActiveCanvas in your Rails app."
|
|
15
|
+
say ""
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def install_migrations
|
|
19
|
+
say "Step 1: Database Migrations", :yellow
|
|
20
|
+
say "-" * 40
|
|
21
|
+
|
|
22
|
+
if yes_no?("Copy ActiveCanvas migrations to your app?", default: true)
|
|
23
|
+
rake "active_canvas:install:migrations"
|
|
24
|
+
say "✓ Migrations copied", :green
|
|
25
|
+
|
|
26
|
+
if yes_no?("Run migrations now?", default: true)
|
|
27
|
+
rake "db:migrate"
|
|
28
|
+
say "✓ Migrations completed", :green
|
|
29
|
+
else
|
|
30
|
+
say "→ Remember to run: bin/rails db:migrate", :yellow
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
say "→ Skipped. Run later: bin/rails active_canvas:install:migrations", :yellow
|
|
34
|
+
end
|
|
35
|
+
say ""
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def choose_css_framework
|
|
39
|
+
say "Step 2: CSS Framework", :yellow
|
|
40
|
+
say "-" * 40
|
|
41
|
+
say "Choose which framework to use in the editor and public pages:"
|
|
42
|
+
say ""
|
|
43
|
+
say " 1. tailwind - Tailwind CSS (recommended)"
|
|
44
|
+
say " 2. bootstrap - Bootstrap 5"
|
|
45
|
+
say " 3. none - No framework"
|
|
46
|
+
say ""
|
|
47
|
+
|
|
48
|
+
framework = ask("Enter choice [tailwind]:")
|
|
49
|
+
framework = "tailwind" if framework.blank?
|
|
50
|
+
framework = framework.downcase.strip
|
|
51
|
+
|
|
52
|
+
@css_framework = case framework
|
|
53
|
+
when "tailwind", "1" then :tailwind
|
|
54
|
+
when "bootstrap", "2" then :bootstrap5
|
|
55
|
+
when "none", "3" then :none
|
|
56
|
+
else :tailwind
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if @css_framework == :tailwind
|
|
60
|
+
setup_tailwind
|
|
61
|
+
elsif @css_framework == :bootstrap5
|
|
62
|
+
setup_bootstrap
|
|
63
|
+
else
|
|
64
|
+
say "→ No framework selected. You can change this in Admin > Settings.", :yellow
|
|
65
|
+
end
|
|
66
|
+
say ""
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def configure_ai_features
|
|
70
|
+
say "Step 3: AI Features", :yellow
|
|
71
|
+
say "-" * 40
|
|
72
|
+
say "ActiveCanvas includes AI-powered features:"
|
|
73
|
+
say " • Text/HTML generation from prompts"
|
|
74
|
+
say " • Image generation (DALL-E)"
|
|
75
|
+
say " • Screenshot to code conversion"
|
|
76
|
+
say ""
|
|
77
|
+
say "Supported providers:", :cyan
|
|
78
|
+
say " • OpenAI (GPT-4, DALL-E) - recommended"
|
|
79
|
+
say " • Anthropic (Claude)"
|
|
80
|
+
say " • OpenRouter (access to many models)"
|
|
81
|
+
say ""
|
|
82
|
+
|
|
83
|
+
@setup_ai = yes_no?("Configure AI API keys now?", default: true)
|
|
84
|
+
|
|
85
|
+
if @setup_ai
|
|
86
|
+
configure_ai_keys
|
|
87
|
+
else
|
|
88
|
+
say "→ Add API keys later in Admin > Settings > AI", :yellow
|
|
89
|
+
end
|
|
90
|
+
say ""
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def create_initializer
|
|
94
|
+
say "Step 4: Configuration", :yellow
|
|
95
|
+
say "-" * 40
|
|
96
|
+
|
|
97
|
+
@mount_path = ask("Mount path [/canvas]:")
|
|
98
|
+
@mount_path = "/canvas" if @mount_path.blank?
|
|
99
|
+
@mount_path = "/#{@mount_path}" unless @mount_path.start_with?("/")
|
|
100
|
+
|
|
101
|
+
template "initializer.rb", "config/initializers/active_canvas.rb"
|
|
102
|
+
say "✓ Created config/initializers/active_canvas.rb", :green
|
|
103
|
+
say ""
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def mount_engine
|
|
107
|
+
say "Step 5: Routes", :yellow
|
|
108
|
+
say "-" * 40
|
|
109
|
+
|
|
110
|
+
routes_file = "config/routes.rb"
|
|
111
|
+
if File.read(routes_file).include?("ActiveCanvas::Engine")
|
|
112
|
+
say "✓ ActiveCanvas::Engine already mounted", :green
|
|
113
|
+
else
|
|
114
|
+
route "mount ActiveCanvas::Engine => '#{@mount_path}'"
|
|
115
|
+
say "✓ Mounted ActiveCanvas at #{@mount_path}", :green
|
|
116
|
+
end
|
|
117
|
+
say ""
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def sync_ai_models
|
|
121
|
+
if @setup_ai && yes_no?("Sync AI models now? (requires API keys in ENV)", default: false)
|
|
122
|
+
say ""
|
|
123
|
+
say "Syncing AI models...", :cyan
|
|
124
|
+
rake "active_canvas:sync_models"
|
|
125
|
+
say "✓ AI models synced", :green
|
|
126
|
+
end
|
|
127
|
+
say ""
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def show_completion
|
|
131
|
+
say "=" * 60, :green
|
|
132
|
+
say " Setup Complete!", :green
|
|
133
|
+
say "=" * 60, :green
|
|
134
|
+
say ""
|
|
135
|
+
say "Next steps:", :yellow
|
|
136
|
+
say ""
|
|
137
|
+
say " 1. Start your Rails server:"
|
|
138
|
+
say " $ bin/rails server", :cyan
|
|
139
|
+
say ""
|
|
140
|
+
say " 2. Visit the admin panel:"
|
|
141
|
+
say " http://localhost:3000#{@mount_path}/admin", :cyan
|
|
142
|
+
say ""
|
|
143
|
+
say " 3. Configure authentication in:"
|
|
144
|
+
say " config/initializers/active_canvas.rb", :cyan
|
|
145
|
+
say ""
|
|
146
|
+
|
|
147
|
+
if @css_framework && @css_framework != :none
|
|
148
|
+
say " 4. Your CSS framework (#{@css_framework}) is configured."
|
|
149
|
+
say " It will be loaded automatically in the editor.", :cyan
|
|
150
|
+
say ""
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
unless @setup_ai
|
|
154
|
+
say " 5. Add AI API keys in Admin > Settings > AI", :cyan
|
|
155
|
+
say " Or via environment variables / Rails credentials", :cyan
|
|
156
|
+
say ""
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
say "Documentation: https://github.com/giovapanasiti/active_canvas"
|
|
160
|
+
say ""
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
# Helper for yes/no prompts with clear defaults
|
|
166
|
+
# @param question [String] The question to ask
|
|
167
|
+
# @param default [Boolean] The default answer (true = Y, false = N)
|
|
168
|
+
# @return [Boolean]
|
|
169
|
+
def yes_no?(question, default: true)
|
|
170
|
+
indicator = default ? "[Y/n]" : "[y/N]"
|
|
171
|
+
answer = ask("#{question} #{indicator}")
|
|
172
|
+
|
|
173
|
+
return default if answer.blank?
|
|
174
|
+
|
|
175
|
+
answer.downcase.start_with?("y")
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def setup_tailwind
|
|
179
|
+
gemfile_content = File.read(Rails.root.join("Gemfile"))
|
|
180
|
+
|
|
181
|
+
if gemfile_content.include?("tailwindcss-rails")
|
|
182
|
+
say "✓ tailwindcss-rails gem already installed", :green
|
|
183
|
+
else
|
|
184
|
+
if yes_no?("Install tailwindcss-rails gem? (required for Tailwind)", default: true)
|
|
185
|
+
gem "tailwindcss-rails"
|
|
186
|
+
say "✓ Added tailwindcss-rails to Gemfile", :green
|
|
187
|
+
|
|
188
|
+
if yes_no?("Run bundle install now?", default: true)
|
|
189
|
+
run "bundle install"
|
|
190
|
+
say "✓ Bundle installed", :green
|
|
191
|
+
else
|
|
192
|
+
say "→ Remember to run: bundle install", :yellow
|
|
193
|
+
end
|
|
194
|
+
else
|
|
195
|
+
say "→ You'll need to add tailwindcss-rails gem manually", :yellow
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def setup_bootstrap
|
|
201
|
+
say "✓ Bootstrap 5 will be loaded from CDN", :green
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def configure_ai_keys
|
|
205
|
+
say ""
|
|
206
|
+
@openai_key = ask("OpenAI API key (leave blank to skip):")
|
|
207
|
+
@anthropic_key = ask("Anthropic API key (leave blank to skip):")
|
|
208
|
+
@openrouter_key = ask("OpenRouter API key (leave blank to skip):")
|
|
209
|
+
|
|
210
|
+
if @openai_key.blank? && @anthropic_key.blank? && @openrouter_key.blank?
|
|
211
|
+
say "→ No API keys provided. Add them later in Admin > Settings > AI", :yellow
|
|
212
|
+
return
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
say ""
|
|
216
|
+
say "How do you want to store the API keys?", :cyan
|
|
217
|
+
say " 1. Environment variables (recommended for production)"
|
|
218
|
+
say " 2. Rails credentials (encrypted)"
|
|
219
|
+
say ""
|
|
220
|
+
|
|
221
|
+
storage = ask("Enter choice [1]:")
|
|
222
|
+
storage = "1" if storage.blank?
|
|
223
|
+
|
|
224
|
+
if storage == "2"
|
|
225
|
+
store_keys_in_credentials
|
|
226
|
+
else
|
|
227
|
+
store_keys_in_env
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def store_keys_in_env
|
|
232
|
+
say ""
|
|
233
|
+
say "Add these to your .env or environment:", :yellow
|
|
234
|
+
say ""
|
|
235
|
+
say " export OPENAI_API_KEY=\"#{@openai_key}\"" if @openai_key.present?
|
|
236
|
+
say " export ANTHROPIC_API_KEY=\"#{@anthropic_key}\"" if @anthropic_key.present?
|
|
237
|
+
say " export OPENROUTER_API_KEY=\"#{@openrouter_key}\"" if @openrouter_key.present?
|
|
238
|
+
say ""
|
|
239
|
+
|
|
240
|
+
# Update initializer template vars
|
|
241
|
+
@ai_openai_env = true if @openai_key.present?
|
|
242
|
+
@ai_anthropic_env = true if @anthropic_key.present?
|
|
243
|
+
@ai_openrouter_env = true if @openrouter_key.present?
|
|
244
|
+
|
|
245
|
+
say "✓ Remember to set these environment variables before starting the server", :green
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def store_keys_in_credentials
|
|
249
|
+
say ""
|
|
250
|
+
say "Add these to your Rails credentials (bin/rails credentials:edit):", :yellow
|
|
251
|
+
say ""
|
|
252
|
+
say "active_canvas:"
|
|
253
|
+
say " openai_api_key: #{@openai_key}" if @openai_key.present?
|
|
254
|
+
say " anthropic_api_key: #{@anthropic_key}" if @anthropic_key.present?
|
|
255
|
+
say " openrouter_api_key: #{@openrouter_key}" if @openrouter_key.present?
|
|
256
|
+
say ""
|
|
257
|
+
|
|
258
|
+
@ai_use_credentials = true
|
|
259
|
+
say "✓ Remember to add these to your credentials file", :green
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# ActiveCanvas Configuration
|
|
2
|
+
# Generated by: bin/rails generate active_canvas:install
|
|
3
|
+
# Documentation: https://github.com/giovapanasiti/active_canvas
|
|
4
|
+
|
|
5
|
+
ActiveCanvas.configure do |config|
|
|
6
|
+
# ==========================================================================
|
|
7
|
+
# Authentication (REQUIRED for production!)
|
|
8
|
+
# ==========================================================================
|
|
9
|
+
#
|
|
10
|
+
# IMPORTANT: Configure one of these options before deploying to production.
|
|
11
|
+
# The admin interface will be inaccessible in production until authentication
|
|
12
|
+
# is configured.
|
|
13
|
+
|
|
14
|
+
# --------------------------------------------------------------------------
|
|
15
|
+
# Option 1: Use your app's authentication method (recommended for Devise)
|
|
16
|
+
# --------------------------------------------------------------------------
|
|
17
|
+
# config.authenticate_admin = :authenticate_user!
|
|
18
|
+
# Or with admin check:
|
|
19
|
+
# config.authenticate_admin = -> {
|
|
20
|
+
# redirect_to main_app.login_path, alert: "Please log in" unless current_user&.admin?
|
|
21
|
+
# }
|
|
22
|
+
|
|
23
|
+
# --------------------------------------------------------------------------
|
|
24
|
+
# Option 2: Inherit from your existing admin controller
|
|
25
|
+
# --------------------------------------------------------------------------
|
|
26
|
+
# This is useful if you already have an admin area with authentication.
|
|
27
|
+
# ActiveCanvas admin controllers will inherit from your controller.
|
|
28
|
+
# config.admin_parent_controller = "Admin::ApplicationController"
|
|
29
|
+
|
|
30
|
+
# --------------------------------------------------------------------------
|
|
31
|
+
# Option 3: HTTP Basic Authentication
|
|
32
|
+
# --------------------------------------------------------------------------
|
|
33
|
+
# Simple password protection. Good for staging or simple setups.
|
|
34
|
+
# IMPORTANT: Use strong credentials and store password in credentials.yml.enc
|
|
35
|
+
config.authenticate_admin = :http_basic_auth
|
|
36
|
+
config.http_basic_user = "admin"
|
|
37
|
+
config.http_basic_password = ENV.fetch("ACTIVE_CANVAS_PASSWORD", "change_me_in_production!")
|
|
38
|
+
|
|
39
|
+
# --------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
# Public pages authentication (optional, nil = public access)
|
|
42
|
+
# config.authenticate_public = nil
|
|
43
|
+
|
|
44
|
+
# Method to get current user (for version tracking, AI features, etc.)
|
|
45
|
+
config.current_user_method = :current_user
|
|
46
|
+
|
|
47
|
+
# ==========================================================================
|
|
48
|
+
# CSS Framework
|
|
49
|
+
# ==========================================================================
|
|
50
|
+
<% if @css_framework && @css_framework != :none %>
|
|
51
|
+
|
|
52
|
+
# Options: :tailwind, :bootstrap5, :none
|
|
53
|
+
config.css_framework = :<%= @css_framework %>
|
|
54
|
+
<% else %>
|
|
55
|
+
|
|
56
|
+
# Options: :tailwind, :bootstrap5, :none
|
|
57
|
+
# config.css_framework = :tailwind
|
|
58
|
+
<% end %>
|
|
59
|
+
|
|
60
|
+
# ==========================================================================
|
|
61
|
+
# Media Uploads
|
|
62
|
+
# ==========================================================================
|
|
63
|
+
|
|
64
|
+
config.enable_uploads = true
|
|
65
|
+
config.max_upload_size = 10.megabytes
|
|
66
|
+
config.allowed_content_types = %w[
|
|
67
|
+
image/jpeg
|
|
68
|
+
image/png
|
|
69
|
+
image/gif
|
|
70
|
+
image/webp
|
|
71
|
+
image/avif
|
|
72
|
+
application/pdf
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# SVG uploads are disabled by default due to XSS risks.
|
|
76
|
+
# Only enable if you trust all content authors.
|
|
77
|
+
# config.allow_svg_uploads = false
|
|
78
|
+
|
|
79
|
+
# Active Storage service (nil = default service from storage.yml)
|
|
80
|
+
# config.storage_service = :amazon
|
|
81
|
+
|
|
82
|
+
# Public uploads: when false (default), uses signed URLs with expiration.
|
|
83
|
+
# When true, uploads are world-readable. Only enable for truly public content.
|
|
84
|
+
# config.public_uploads = false
|
|
85
|
+
|
|
86
|
+
# ==========================================================================
|
|
87
|
+
# Editor Features
|
|
88
|
+
# ==========================================================================
|
|
89
|
+
|
|
90
|
+
config.enable_ai_features = true # AI text/image generation
|
|
91
|
+
config.enable_code_editor = true # Code/HTML editing
|
|
92
|
+
config.enable_asset_manager = true # Media library
|
|
93
|
+
|
|
94
|
+
# Auto-save interval in seconds (0 = disabled)
|
|
95
|
+
config.autosave_interval = 60
|
|
96
|
+
|
|
97
|
+
# ==========================================================================
|
|
98
|
+
# Version History
|
|
99
|
+
# ==========================================================================
|
|
100
|
+
|
|
101
|
+
# Maximum versions to keep per page (0 = unlimited)
|
|
102
|
+
config.max_versions_per_page = 50
|
|
103
|
+
|
|
104
|
+
# ==========================================================================
|
|
105
|
+
# Security
|
|
106
|
+
# ==========================================================================
|
|
107
|
+
|
|
108
|
+
# Sanitize HTML content on save (enabled by default for security)
|
|
109
|
+
# Disable only if all content authors are fully trusted
|
|
110
|
+
# config.sanitize_content = true
|
|
111
|
+
|
|
112
|
+
# AI rate limiting (requests per minute per IP)
|
|
113
|
+
# config.ai_rate_limit_per_minute = 30
|
|
114
|
+
|
|
115
|
+
# AI streaming timeouts
|
|
116
|
+
# config.ai_stream_timeout = 5.minutes
|
|
117
|
+
# config.ai_stream_idle_timeout = 30.seconds
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# ==========================================================================
|
|
121
|
+
# AI API Keys Configuration
|
|
122
|
+
# ==========================================================================
|
|
123
|
+
# Configure your AI provider API keys. Keys are encrypted in the database.
|
|
124
|
+
# You can set them here on boot, or add them via Admin > Settings > AI.
|
|
125
|
+
|
|
126
|
+
Rails.application.config.after_initialize do
|
|
127
|
+
# Skip if database isn't ready (migrations pending, etc.)
|
|
128
|
+
next unless ActiveRecord::Base.connection.table_exists?("active_canvas_settings")
|
|
129
|
+
<% if @ai_use_credentials %>
|
|
130
|
+
|
|
131
|
+
# Using Rails credentials
|
|
132
|
+
credentials = Rails.application.credentials.active_canvas || {}
|
|
133
|
+
ActiveCanvas::Setting.ai_openai_api_key = credentials[:openai_api_key] if credentials[:openai_api_key].present?
|
|
134
|
+
ActiveCanvas::Setting.ai_anthropic_api_key = credentials[:anthropic_api_key] if credentials[:anthropic_api_key].present?
|
|
135
|
+
ActiveCanvas::Setting.ai_openrouter_api_key = credentials[:openrouter_api_key] if credentials[:openrouter_api_key].present?
|
|
136
|
+
<% elsif @ai_openai_env || @ai_anthropic_env || @ai_openrouter_env %>
|
|
137
|
+
|
|
138
|
+
# Using environment variables
|
|
139
|
+
<% if @ai_openai_env %>
|
|
140
|
+
ActiveCanvas::Setting.ai_openai_api_key = ENV["OPENAI_API_KEY"] if ENV["OPENAI_API_KEY"].present?
|
|
141
|
+
<% end %>
|
|
142
|
+
<% if @ai_anthropic_env %>
|
|
143
|
+
ActiveCanvas::Setting.ai_anthropic_api_key = ENV["ANTHROPIC_API_KEY"] if ENV["ANTHROPIC_API_KEY"].present?
|
|
144
|
+
<% end %>
|
|
145
|
+
<% if @ai_openrouter_env %>
|
|
146
|
+
ActiveCanvas::Setting.ai_openrouter_api_key = ENV["OPENROUTER_API_KEY"] if ENV["OPENROUTER_API_KEY"].present?
|
|
147
|
+
<% end %>
|
|
148
|
+
<% else %>
|
|
149
|
+
|
|
150
|
+
# Uncomment and configure your preferred method:
|
|
151
|
+
#
|
|
152
|
+
# Option 1: Environment variables
|
|
153
|
+
# ActiveCanvas::Setting.ai_openai_api_key = ENV["OPENAI_API_KEY"] if ENV["OPENAI_API_KEY"].present?
|
|
154
|
+
# ActiveCanvas::Setting.ai_anthropic_api_key = ENV["ANTHROPIC_API_KEY"] if ENV["ANTHROPIC_API_KEY"].present?
|
|
155
|
+
# ActiveCanvas::Setting.ai_openrouter_api_key = ENV["OPENROUTER_API_KEY"] if ENV["OPENROUTER_API_KEY"].present?
|
|
156
|
+
#
|
|
157
|
+
# Option 2: Rails credentials
|
|
158
|
+
# credentials = Rails.application.credentials.active_canvas || {}
|
|
159
|
+
# ActiveCanvas::Setting.ai_openai_api_key = credentials[:openai_api_key] if credentials[:openai_api_key].present?
|
|
160
|
+
<% end %>
|
|
161
|
+
rescue ActiveRecord::NoDatabaseError, ActiveRecord::StatementInvalid
|
|
162
|
+
# Database not ready yet, keys will need to be set via Admin UI
|
|
163
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
namespace :active_canvas do
|
|
2
|
+
namespace :install do
|
|
3
|
+
desc "Copy migrations from active_canvas to application"
|
|
4
|
+
task :migrations do
|
|
5
|
+
source = ActiveCanvas::Engine.root.join("db/migrate")
|
|
6
|
+
destination = ActiveRecord::Tasks::DatabaseTasks.migrations_paths.first
|
|
7
|
+
|
|
8
|
+
ActiveRecord::Migration.copy(destination, { active_canvas: source })
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
desc "Sync AI models from configured providers (OpenAI, Anthropic, OpenRouter)"
|
|
13
|
+
task sync_models: :environment do
|
|
14
|
+
# unless ActiveCanvas::AiConfiguration.ruby_llm_available?
|
|
15
|
+
# puts "RubyLLM gem is not available. Add 'ruby_llm' to your Gemfile."
|
|
16
|
+
# exit 1
|
|
17
|
+
# end
|
|
18
|
+
|
|
19
|
+
unless ActiveCanvas::AiConfiguration.configured?
|
|
20
|
+
puts "No API keys configured."
|
|
21
|
+
puts "Add keys via Settings > AI or set them directly:"
|
|
22
|
+
puts " ActiveCanvas::Setting.ai_openai_api_key = 'sk-...'"
|
|
23
|
+
puts " ActiveCanvas::Setting.ai_anthropic_api_key = 'sk-ant-...'"
|
|
24
|
+
puts " ActiveCanvas::Setting.ai_openrouter_api_key = 'sk-or-...'"
|
|
25
|
+
exit 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
providers = ActiveCanvas::AiConfiguration.configured_providers
|
|
29
|
+
puts "Configured providers: #{providers.join(', ')}"
|
|
30
|
+
puts "Syncing models..."
|
|
31
|
+
|
|
32
|
+
count = ActiveCanvas::AiModels.refresh!
|
|
33
|
+
puts "Done! Synced #{count} models."
|
|
34
|
+
|
|
35
|
+
# Show breakdown by type
|
|
36
|
+
text_count = ActiveCanvas::AiModel.active.text_models.count
|
|
37
|
+
image_count = ActiveCanvas::AiModel.active.image_models.count
|
|
38
|
+
vision_count = ActiveCanvas::AiModel.active.vision_models.count
|
|
39
|
+
|
|
40
|
+
puts ""
|
|
41
|
+
puts "Breakdown:"
|
|
42
|
+
puts " Text models: #{text_count}"
|
|
43
|
+
puts " Image models: #{image_count}"
|
|
44
|
+
puts " Vision models: #{vision_count}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
desc "List synced AI models"
|
|
48
|
+
task list_models: :environment do
|
|
49
|
+
unless ActiveCanvas::AiModel.exists?
|
|
50
|
+
puts "No models synced yet. Run: rails active_canvas:sync_models"
|
|
51
|
+
exit 0
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
puts "=== Text/Chat Models ==="
|
|
55
|
+
ActiveCanvas::AiModel.active.text_models.order(:provider, :name).each do |m|
|
|
56
|
+
vision = m.supports_vision? ? " [vision]" : ""
|
|
57
|
+
puts " #{m.provider.ljust(12)} #{m.display_name}#{vision}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
puts ""
|
|
61
|
+
puts "=== Image Models ==="
|
|
62
|
+
ActiveCanvas::AiModel.active.image_models.order(:provider, :name).each do |m|
|
|
63
|
+
puts " #{m.provider.ljust(12)} #{m.display_name}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
puts ""
|
|
67
|
+
puts "Total: #{ActiveCanvas::AiModel.active.count} models"
|
|
68
|
+
end
|
|
69
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: active_canvas
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Giovanni Panasiti
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-02-26 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 8.0.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 8.0.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ruby_llm
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.0'
|
|
40
|
+
description: ActiveCanvas provides a simple CMS for creating and managing static pages
|
|
41
|
+
with an admin interface
|
|
42
|
+
email:
|
|
43
|
+
- giova.panasiti@gmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- MIT-LICENSE
|
|
49
|
+
- README.md
|
|
50
|
+
- Rakefile
|
|
51
|
+
- app/assets/javascripts/active_canvas/editor.js
|
|
52
|
+
- app/assets/javascripts/active_canvas/editor/ai_panel.js
|
|
53
|
+
- app/assets/javascripts/active_canvas/editor/asset_manager.js
|
|
54
|
+
- app/assets/javascripts/active_canvas/editor/blocks.js
|
|
55
|
+
- app/assets/javascripts/active_canvas/editor/code_panel.js
|
|
56
|
+
- app/assets/javascripts/active_canvas/editor/component_toolbar.js
|
|
57
|
+
- app/assets/javascripts/active_canvas/editor/panels.js
|
|
58
|
+
- app/assets/javascripts/active_canvas/editor/utils.js
|
|
59
|
+
- app/assets/stylesheets/active_canvas/application.css
|
|
60
|
+
- app/assets/stylesheets/active_canvas/editor.css
|
|
61
|
+
- app/controllers/active_canvas/admin/ai_controller.rb
|
|
62
|
+
- app/controllers/active_canvas/admin/application_controller.rb
|
|
63
|
+
- app/controllers/active_canvas/admin/media_controller.rb
|
|
64
|
+
- app/controllers/active_canvas/admin/page_types_controller.rb
|
|
65
|
+
- app/controllers/active_canvas/admin/page_versions_controller.rb
|
|
66
|
+
- app/controllers/active_canvas/admin/pages_controller.rb
|
|
67
|
+
- app/controllers/active_canvas/admin/partials_controller.rb
|
|
68
|
+
- app/controllers/active_canvas/admin/settings_controller.rb
|
|
69
|
+
- app/controllers/active_canvas/application_controller.rb
|
|
70
|
+
- app/controllers/active_canvas/pages_controller.rb
|
|
71
|
+
- app/controllers/concerns/active_canvas/current_user.rb
|
|
72
|
+
- app/controllers/concerns/active_canvas/rate_limitable.rb
|
|
73
|
+
- app/controllers/concerns/active_canvas/tailwind_compilation.rb
|
|
74
|
+
- app/helpers/active_canvas/application_helper.rb
|
|
75
|
+
- app/jobs/active_canvas/application_job.rb
|
|
76
|
+
- app/jobs/active_canvas/compile_tailwind_job.rb
|
|
77
|
+
- app/mailers/active_canvas/application_mailer.rb
|
|
78
|
+
- app/models/active_canvas/ai_model.rb
|
|
79
|
+
- app/models/active_canvas/application_record.rb
|
|
80
|
+
- app/models/active_canvas/media.rb
|
|
81
|
+
- app/models/active_canvas/page.rb
|
|
82
|
+
- app/models/active_canvas/page_type.rb
|
|
83
|
+
- app/models/active_canvas/page_version.rb
|
|
84
|
+
- app/models/active_canvas/partial.rb
|
|
85
|
+
- app/models/active_canvas/setting.rb
|
|
86
|
+
- app/services/active_canvas/ai_configuration.rb
|
|
87
|
+
- app/services/active_canvas/ai_models.rb
|
|
88
|
+
- app/services/active_canvas/ai_service.rb
|
|
89
|
+
- app/services/active_canvas/content_sanitizer.rb
|
|
90
|
+
- app/services/active_canvas/tailwind_compiler.rb
|
|
91
|
+
- app/views/active_canvas/admin/media/index.html.erb
|
|
92
|
+
- app/views/active_canvas/admin/media/show.html.erb
|
|
93
|
+
- app/views/active_canvas/admin/page_types/_form.html.erb
|
|
94
|
+
- app/views/active_canvas/admin/page_types/edit.html.erb
|
|
95
|
+
- app/views/active_canvas/admin/page_types/index.html.erb
|
|
96
|
+
- app/views/active_canvas/admin/page_types/new.html.erb
|
|
97
|
+
- app/views/active_canvas/admin/page_types/show.html.erb
|
|
98
|
+
- app/views/active_canvas/admin/page_versions/show.html.erb
|
|
99
|
+
- app/views/active_canvas/admin/pages/_form.html.erb
|
|
100
|
+
- app/views/active_canvas/admin/pages/content.html.erb
|
|
101
|
+
- app/views/active_canvas/admin/pages/edit.html.erb
|
|
102
|
+
- app/views/active_canvas/admin/pages/editor.html.erb
|
|
103
|
+
- app/views/active_canvas/admin/pages/index.html.erb
|
|
104
|
+
- app/views/active_canvas/admin/pages/new.html.erb
|
|
105
|
+
- app/views/active_canvas/admin/pages/show.html.erb
|
|
106
|
+
- app/views/active_canvas/admin/pages/versions.html.erb
|
|
107
|
+
- app/views/active_canvas/admin/partials/edit.html.erb
|
|
108
|
+
- app/views/active_canvas/admin/partials/editor.html.erb
|
|
109
|
+
- app/views/active_canvas/admin/partials/index.html.erb
|
|
110
|
+
- app/views/active_canvas/admin/settings/show.html.erb
|
|
111
|
+
- app/views/active_canvas/pages/no_homepage.html.erb
|
|
112
|
+
- app/views/active_canvas/pages/show.html.erb
|
|
113
|
+
- app/views/layouts/active_canvas/admin/application.html.erb
|
|
114
|
+
- app/views/layouts/active_canvas/admin/editor.html.erb
|
|
115
|
+
- app/views/layouts/active_canvas/application.html.erb
|
|
116
|
+
- config/routes.rb
|
|
117
|
+
- db/migrate/20260202000001_create_active_canvas_tables.rb
|
|
118
|
+
- db/migrate/20260202000002_create_active_canvas_ai_models.rb
|
|
119
|
+
- lib/active_canvas.rb
|
|
120
|
+
- lib/active_canvas/configuration.rb
|
|
121
|
+
- lib/active_canvas/engine.rb
|
|
122
|
+
- lib/active_canvas/version.rb
|
|
123
|
+
- lib/generators/active_canvas/install/install_generator.rb
|
|
124
|
+
- lib/generators/active_canvas/install/templates/initializer.rb.tt
|
|
125
|
+
- lib/tasks/active_canvas_tasks.rake
|
|
126
|
+
homepage: https://github.com/giovapanasiti/active_canvas
|
|
127
|
+
licenses:
|
|
128
|
+
- MIT
|
|
129
|
+
metadata:
|
|
130
|
+
homepage_uri: https://github.com/giovapanasiti/active_canvas
|
|
131
|
+
source_code_uri: https://github.com/giovapanasiti/active_canvas
|
|
132
|
+
changelog_uri: https://github.com/giovapanasiti/active_canvas/blob/master/CHANGELOG.md
|
|
133
|
+
rdoc_options: []
|
|
134
|
+
require_paths:
|
|
135
|
+
- lib
|
|
136
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
137
|
+
requirements:
|
|
138
|
+
- - ">="
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
version: '0'
|
|
141
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
requirements: []
|
|
147
|
+
rubygems_version: 3.6.2
|
|
148
|
+
specification_version: 4
|
|
149
|
+
summary: A mountable Rails CMS engine for managing static pages
|
|
150
|
+
test_files: []
|