one-for-all-framework 1.0.0 → 2.0.0
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/README.md +28 -8
- data/app/controllers/api_controller.rb +1 -1
- data/app/controllers/application_controller.rb +2 -2
- data/app/controllers/dashboard_controller.rb +2 -2
- data/app/views/docs.erb +47 -8
- data/app/views/index.erb +1 -1
- data/app/views/layout.erb +275 -15
- data/app/views/login.erb +1 -1
- data/bin/ofa +26 -6
- data/config/features.json +1 -1
- data/db/development.sqlite3 +0 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 55f0f757d3582b86a7b3c4f3f55ed0074b66f680540d38dc6b7e0bcda9f484ed
|
|
4
|
+
data.tar.gz: 74c31da1387582d610f8b78aa5f174d22517921719623fc74f57e4c71adb077e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b28e6c8a6530c27c2d25121cc9ecf7bafaa2cb8c16f3d702893918bcf02e5f29890aeb35a2112293592c5104bb8ade48a51c468cd4712921e033965e6dde8b78
|
|
7
|
+
data.tar.gz: 3f69537492a02ddf4c559b358d6af8ebaef7e95ddaf034899ba84ba95e5e4a59c20764f44ca41334b50b49f8d51617ebab0ddc06600ffbc5ce9c3f7580484e9a
|
data/README.md
CHANGED
|
@@ -56,16 +56,36 @@ Your app is now live at `http://localhost:3000` ⚡
|
|
|
56
56
|
|
|
57
57
|
The `ofa` CLI is your best friend. Use it to manage your entire application lifecycle:
|
|
58
58
|
|
|
59
|
+
### Project Management
|
|
59
60
|
| Command | Description |
|
|
60
61
|
| :--- | :--- |
|
|
61
|
-
| `ofa new NAME` | Create a
|
|
62
|
-
| `ofa
|
|
63
|
-
| `ofa
|
|
64
|
-
| `ofa
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
|
68
|
-
|
|
|
62
|
+
| `ofa new NAME [TYPE]` | Create a new project with automatic bundle install. |
|
|
63
|
+
| `ofa init [TYPE]` | Initialize project in current folder with interactive wizard. |
|
|
64
|
+
| `ofa run` | Start the high-performance development server. |
|
|
65
|
+
| `ofa deploy` | Deploy project to production (Railway/Docker ready). |
|
|
66
|
+
|
|
67
|
+
### Generators (Scaffolding)
|
|
68
|
+
| Command | Description |
|
|
69
|
+
| :--- | :--- |
|
|
70
|
+
| `ofa g controller NAME` | Generate a RESTful controller in `app/controllers`. |
|
|
71
|
+
| `ofa g model NAME` | Generate a database model and migration. |
|
|
72
|
+
| `ofa g migration NAME` | Generate a new database migration file. |
|
|
73
|
+
| `ofa g post TITLE` | Create a new Markdown post (Supports `--category`, `--author`). |
|
|
74
|
+
|
|
75
|
+
### Configuration & Features
|
|
76
|
+
| Command | Description |
|
|
77
|
+
| :--- | :--- |
|
|
78
|
+
| `ofa type [TYPE]` | Set app type: `portfolio`, `blog`, or `landing_page`. |
|
|
79
|
+
| `ofa theme [THEME]` | Set UI theme: `light_glass`, `dark_glass`, `cyber_sidebar`, etc. |
|
|
80
|
+
| `ofa feature enable [F]` | Toggle features like `cms` or `auth`. |
|
|
81
|
+
| `ofa storage [local\|cloudinary]` | Set image storage provider. |
|
|
82
|
+
| `ofa reset-password USR PWD`| Reset or create admin account credentials. |
|
|
83
|
+
|
|
84
|
+
### Database Management
|
|
85
|
+
| Command | Description |
|
|
86
|
+
| :--- | :--- |
|
|
87
|
+
| `ofa db switch [ADAPTER]` | Switch DB: `sqlite`, `mysql`, `postgres`, `mongodb`. |
|
|
88
|
+
| `ofa db migrate` | Run all pending database migrations. |
|
|
69
89
|
|
|
70
90
|
---
|
|
71
91
|
|
|
@@ -9,7 +9,7 @@ class ApiController < ApplicationController
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def status
|
|
12
|
-
render_json({ status: 'ok', version: '
|
|
12
|
+
render_json({ status: 'ok', version: '2.0.0', type: FEATURES_CONFIG['type'] })
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def halt_error(message, status: 400)
|
|
@@ -58,7 +58,7 @@ class ApplicationController
|
|
|
58
58
|
rescue => e
|
|
59
59
|
puts "⚠ Error deleting from Cloudinary: #{e.message}"
|
|
60
60
|
end
|
|
61
|
-
elsif url.start_with?('/
|
|
61
|
+
elsif url.start_with?('/images/uploads/')
|
|
62
62
|
path = File.join(APP_ROOT, 'public', url)
|
|
63
63
|
FileUtils.rm(path) if File.exist?(path) rescue nil
|
|
64
64
|
end
|
|
@@ -71,7 +71,7 @@ class ApplicationController
|
|
|
71
71
|
urls.each { |url| delete_from_storage(url) }
|
|
72
72
|
|
|
73
73
|
# Also find local uploads
|
|
74
|
-
local_urls = content.scan(/\/
|
|
74
|
+
local_urls = content.scan(/\/images\/uploads\/[^\s\)]+/)
|
|
75
75
|
local_urls.each { |url| delete_from_storage(url) }
|
|
76
76
|
end
|
|
77
77
|
end
|
|
@@ -27,9 +27,9 @@ class DashboardController < ApplicationController
|
|
|
27
27
|
end
|
|
28
28
|
else
|
|
29
29
|
filename = "#{Time.now.to_i}_#{file[:filename]}"
|
|
30
|
-
target = File.join(APP_ROOT, 'public/
|
|
30
|
+
target = File.join(APP_ROOT, 'public/images/uploads', filename)
|
|
31
31
|
FileUtils.cp(file[:tempfile].path, target)
|
|
32
|
-
@res.body << { url: "/
|
|
32
|
+
@res.body << { url: "/images/uploads/#{filename}" }.to_json
|
|
33
33
|
end
|
|
34
34
|
else
|
|
35
35
|
@res.status = 400
|
data/app/views/docs.erb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="space-y-12 pb-20">
|
|
2
2
|
<div class="text-left">
|
|
3
|
-
<div class="badge-premium">Documentation
|
|
3
|
+
<div class="badge-premium">Documentation v2.0</div>
|
|
4
4
|
<h1 class="text-5xl font-black tracking-tighter mb-4 text-slate-700 dark:text-white">Framework <span class="text-primary">Guide</span></h1>
|
|
5
5
|
<p class="text-xl text-slate-500 max-w-2xl leading-relaxed">Everything you need to know about building premium web applications with the One-For-All framework.</p>
|
|
6
6
|
</div>
|
|
@@ -45,21 +45,58 @@
|
|
|
45
45
|
</tr>
|
|
46
46
|
</thead>
|
|
47
47
|
<tbody class="divide-y divide-white/5">
|
|
48
|
+
<tr class="bg-slate-500/5"><td colspan="2" class="px-6 py-2 text-[10px] font-black uppercase tracking-tighter text-slate-400">Project Management</td></tr>
|
|
49
|
+
<tr>
|
|
50
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa new [name]</td>
|
|
51
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Creates a new project from scratch.</td>
|
|
52
|
+
</tr>
|
|
48
53
|
<tr>
|
|
49
54
|
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa init</td>
|
|
50
|
-
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Initializes
|
|
55
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Initializes environment & DB via wizard.</td>
|
|
51
56
|
</tr>
|
|
52
57
|
<tr>
|
|
53
58
|
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa run</td>
|
|
54
|
-
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Starts the
|
|
59
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Starts the development server.</td>
|
|
55
60
|
</tr>
|
|
61
|
+
<tr>
|
|
62
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa deploy</td>
|
|
63
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Deploys to production environment.</td>
|
|
64
|
+
</tr>
|
|
65
|
+
|
|
66
|
+
<tr class="bg-slate-500/5"><td colspan="2" class="px-6 py-2 text-[10px] font-black uppercase tracking-tighter text-slate-400">Generators</td></tr>
|
|
56
67
|
<tr>
|
|
57
68
|
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa g controller [name]</td>
|
|
58
|
-
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Generates a
|
|
69
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Generates a RESTful controller.</td>
|
|
70
|
+
</tr>
|
|
71
|
+
<tr>
|
|
72
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa g model [name]</td>
|
|
73
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Generates a model & migration.</td>
|
|
74
|
+
</tr>
|
|
75
|
+
<tr>
|
|
76
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa g migration [name]</td>
|
|
77
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Creates a new schema migration.</td>
|
|
78
|
+
</tr>
|
|
79
|
+
<tr>
|
|
80
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa g post [title]</td>
|
|
81
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Creates a Markdown-based blog post.</td>
|
|
82
|
+
</tr>
|
|
83
|
+
|
|
84
|
+
<tr class="bg-slate-500/5"><td colspan="2" class="px-6 py-2 text-[10px] font-black uppercase tracking-tighter text-slate-400">Configuration</td></tr>
|
|
85
|
+
<tr>
|
|
86
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa theme [name]</td>
|
|
87
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Switch between UI themes instantly.</td>
|
|
88
|
+
</tr>
|
|
89
|
+
<tr>
|
|
90
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa type [type]</td>
|
|
91
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Change app mode (blog, portfolio, etc).</td>
|
|
92
|
+
</tr>
|
|
93
|
+
<tr>
|
|
94
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa storage [name]</td>
|
|
95
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Configure Local or Cloudinary storage.</td>
|
|
59
96
|
</tr>
|
|
60
97
|
<tr>
|
|
61
|
-
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa
|
|
62
|
-
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">
|
|
98
|
+
<td class="px-6 py-4 font-mono text-sm text-primary">./ofa db switch [name]</td>
|
|
99
|
+
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">Switch DB adapter (SQLite, MySQL, etc).</td>
|
|
63
100
|
</tr>
|
|
64
101
|
</tbody>
|
|
65
102
|
</table>
|
|
@@ -79,8 +116,10 @@
|
|
|
79
116
|
/models # Database entities
|
|
80
117
|
/views # ERB templates
|
|
81
118
|
/config
|
|
82
|
-
/database.
|
|
83
|
-
/
|
|
119
|
+
/database.json # DB configurations
|
|
120
|
+
/features.json # Feature toggles
|
|
121
|
+
/db
|
|
122
|
+
/migrations # Schema versions
|
|
84
123
|
/public
|
|
85
124
|
/images # Static assets
|
|
86
125
|
/css # Custom styles
|
data/app/views/index.erb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div class="text-center space-y-6 max-w-2xl mx-auto py-12">
|
|
2
2
|
<div class="inline-flex items-center px-4 py-1.5 rounded-full bg-primary/10 border border-primary/20 text-primary text-xs font-bold uppercase tracking-wider animate-pulse">
|
|
3
|
-
Framework Version
|
|
3
|
+
Framework Version 2.0.0
|
|
4
4
|
</div>
|
|
5
5
|
|
|
6
6
|
<h1 class="text-5xl md:text-7xl font-black tracking-tight leading-tight">
|
data/app/views/layout.erb
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<!-- Fonts & Icons -->
|
|
10
10
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
11
11
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
12
|
-
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Fira+Code&display=swap" rel="stylesheet">
|
|
12
|
+
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&family=Fira+Code&family=VT323&display=swap" rel="stylesheet">
|
|
13
13
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
14
14
|
|
|
15
15
|
<!-- Tailwind CSS CDN -->
|
|
@@ -27,13 +27,13 @@
|
|
|
27
27
|
theme: {
|
|
28
28
|
extend: {
|
|
29
29
|
colors: {
|
|
30
|
-
primary: '<%= FEATURES_CONFIG["theme"] == "
|
|
31
|
-
secondary: '<%= FEATURES_CONFIG["theme"] == "
|
|
32
|
-
surface: '<%= FEATURES_CONFIG["theme"] == "
|
|
33
|
-
bg: '<%= FEATURES_CONFIG["theme"] == "
|
|
30
|
+
primary: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#4f46e5" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#f0f" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0f0" : "#818cf8")) %>',
|
|
31
|
+
secondary: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#8b5cf6" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#0ff" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#040" : "#c084fc")) %>',
|
|
32
|
+
surface: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "rgba(255, 255, 255, 0.7)" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "rgba(20, 20, 20, 0.8)" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#050505" : "rgba(17, 24, 39, 0.6)")) %>',
|
|
33
|
+
bg: '<%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#f8fafc" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#000" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0a0a0a" : "#030712")) %>',
|
|
34
34
|
},
|
|
35
35
|
fontFamily: {
|
|
36
|
-
sans: ['Outfit', 'sans-serif'],
|
|
36
|
+
sans: ['<%= FEATURES_CONFIG["theme"] == "retro_terminal" ? "VT323" : "Outfit" %>', 'sans-serif'],
|
|
37
37
|
mono: ['Fira Code', 'monospace'],
|
|
38
38
|
},
|
|
39
39
|
backdropBlur: {
|
|
@@ -46,15 +46,29 @@
|
|
|
46
46
|
|
|
47
47
|
<style>
|
|
48
48
|
:root {
|
|
49
|
-
--primary: <%= FEATURES_CONFIG["theme"] == "
|
|
50
|
-
--secondary: <%= FEATURES_CONFIG["theme"] == "
|
|
51
|
-
--surface: <%= FEATURES_CONFIG["theme"] == "
|
|
52
|
-
--bg-color: <%= FEATURES_CONFIG["theme"] == "
|
|
53
|
-
--glass-border: <%= FEATURES_CONFIG["theme"]
|
|
49
|
+
--primary: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#4f46e5" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#f0f" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0f0" : "#818cf8")) %>;
|
|
50
|
+
--secondary: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#8b5cf6" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#0ff" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#040" : "#c084fc")) %>;
|
|
51
|
+
--surface: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "rgba(255, 255, 255, 0.7)" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "rgba(20, 20, 20, 0.8)" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#050505" : "rgba(17, 24, 39, 0.6)")) %>;
|
|
52
|
+
--bg-color: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "#f8fafc" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#000" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0a0a0a" : "#030712")) %>;
|
|
53
|
+
--glass-border: <%= ["light_glass", "light_sidebar"].include?(FEATURES_CONFIG["theme"]) ? "rgba(0, 0, 0, 0.05)" : (FEATURES_CONFIG["theme"] == "cyber_sidebar" ? "#f0f" : (FEATURES_CONFIG["theme"] == "retro_terminal" ? "#0f0" : "rgba(255, 255, 255, 0.1)")) %>;
|
|
54
|
+
--neo-shadow: 8px 8px 0px 0px var(--primary);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
body {
|
|
57
58
|
background-color: var(--bg-color);
|
|
59
|
+
<% if FEATURES_CONFIG['theme'] == 'cyber_sidebar' %>
|
|
60
|
+
background-image:
|
|
61
|
+
linear-gradient(rgba(255, 0, 255, 0.05) 1px, transparent 1px),
|
|
62
|
+
linear-gradient(90deg, rgba(255, 0, 255, 0.05) 1px, transparent 1px);
|
|
63
|
+
background-size: 50px 50px;
|
|
64
|
+
<% elsif FEATURES_CONFIG['theme'] == 'light_sidebar' %>
|
|
65
|
+
background-image:
|
|
66
|
+
radial-gradient(at 0% 0%, hsla(220,100%,95%,1) 0, transparent 50%),
|
|
67
|
+
radial-gradient(at 100% 0%, hsla(320,100%,95%,0.8) 0, transparent 50%);
|
|
68
|
+
<% elsif FEATURES_CONFIG['theme'] == 'retro_terminal' %>
|
|
69
|
+
background-image: radial-gradient(circle, rgba(0, 255, 0, 0.05) 1px, transparent 1px);
|
|
70
|
+
background-size: 20px 20px;
|
|
71
|
+
<% else %>
|
|
58
72
|
background-image:
|
|
59
73
|
<% if FEATURES_CONFIG['theme'] == 'light_glass' %>
|
|
60
74
|
radial-gradient(at 0% 0%, hsla(220,100%,95%,1) 0, transparent 50%),
|
|
@@ -65,7 +79,25 @@
|
|
|
65
79
|
radial-gradient(at 50% 0%, hsla(225,39%,30%,0.15) 0, transparent 50%),
|
|
66
80
|
radial-gradient(at 100% 0%, hsla(339,49%,30%,0.15) 0, transparent 50%);
|
|
67
81
|
<% end %>
|
|
82
|
+
<% end %>
|
|
68
83
|
background-attachment: fixed;
|
|
84
|
+
<% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
|
|
85
|
+
overflow-x: hidden;
|
|
86
|
+
<% end %>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.neo-card {
|
|
90
|
+
background: var(--surface);
|
|
91
|
+
border: 3px solid var(--primary);
|
|
92
|
+
box-shadow: var(--neo-shadow);
|
|
93
|
+
border-radius: 0px;
|
|
94
|
+
padding: 2rem;
|
|
95
|
+
transition: all 0.2s;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.neo-card:hover {
|
|
99
|
+
transform: translate(-4px, -4px);
|
|
100
|
+
box-shadow: 12px 12px 0px 0px var(--secondary);
|
|
69
101
|
}
|
|
70
102
|
|
|
71
103
|
.glass-panel {
|
|
@@ -184,9 +216,95 @@
|
|
|
184
216
|
.markdown-content h1 { font-size: 2.25rem; font-weight: 800; margin: 2rem 0 1rem; color: var(--primary); }
|
|
185
217
|
.markdown-content p { font-size: 1.125rem; color: #475569; margin-bottom: 1.5rem; }
|
|
186
218
|
.dark .markdown-content p { color: #cbd5e1; }
|
|
219
|
+
|
|
220
|
+
/* Cyber Theme Specifics */
|
|
221
|
+
.cyber-border {
|
|
222
|
+
border: 2px solid var(--primary);
|
|
223
|
+
box-shadow: 4px 4px 0px var(--secondary);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.cyber-btn {
|
|
227
|
+
background: var(--primary);
|
|
228
|
+
color: black;
|
|
229
|
+
font-weight: 900;
|
|
230
|
+
text-transform: uppercase;
|
|
231
|
+
padding: 1rem 2rem;
|
|
232
|
+
border: none;
|
|
233
|
+
box-shadow: 6px 6px 0px var(--secondary);
|
|
234
|
+
transition: all 0.1s;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.cyber-btn:active {
|
|
238
|
+
transform: translate(2px, 2px);
|
|
239
|
+
box-shadow: 4px 4px 0px var(--secondary);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/* Global Text Color for Dark Themes */
|
|
243
|
+
.dark {
|
|
244
|
+
color: #e2e8f0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.dark h1, .dark h2, .dark h3, .dark h4, .dark h5, .dark h6 {
|
|
248
|
+
color: <%= FEATURES_CONFIG['theme'] == 'retro_terminal' ? '#0f0' : '#ffffff' %> !important;
|
|
249
|
+
<% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
|
|
250
|
+
text-shadow: 0 0 10px rgba(0, 255, 0, 0.5);
|
|
251
|
+
text-transform: uppercase;
|
|
252
|
+
letter-spacing: 2px;
|
|
253
|
+
<% end %>
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.dark p, .dark span, .dark li, .dark a {
|
|
257
|
+
color: <%= FEATURES_CONFIG['theme'] == 'retro_terminal' ? '#0f0' : 'rgba(255, 255, 255, 0.9)' %>;
|
|
258
|
+
<% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
|
|
259
|
+
font-family: 'VT323', monospace;
|
|
260
|
+
font-size: 1.25rem;
|
|
261
|
+
<% end %>
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.dark .text-slate-500, .dark .text-slate-600, .dark .text-slate-700 {
|
|
265
|
+
color: <%= FEATURES_CONFIG['theme'] == 'retro_terminal' ? '#0a0' : 'rgba(255, 255, 255, 0.7)' %> !important;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Scanlines for Retro Theme */
|
|
269
|
+
<% if FEATURES_CONFIG['theme'] == 'retro_terminal' %>
|
|
270
|
+
body::before {
|
|
271
|
+
content: " ";
|
|
272
|
+
display: block;
|
|
273
|
+
position: fixed;
|
|
274
|
+
top: 0; left: 0; bottom: 0; right: 0;
|
|
275
|
+
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%),
|
|
276
|
+
linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
|
|
277
|
+
z-index: 999;
|
|
278
|
+
background-size: 100% 2px, 3px 100%;
|
|
279
|
+
pointer-events: none;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@keyframes flicker {
|
|
283
|
+
0% { opacity: 0.97; }
|
|
284
|
+
5% { opacity: 0.95; }
|
|
285
|
+
10% { opacity: 0.9; }
|
|
286
|
+
15% { opacity: 0.95; }
|
|
287
|
+
20% { opacity: 0.98; }
|
|
288
|
+
100% { opacity: 1; }
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.dark main {
|
|
292
|
+
animation: flicker 0.15s infinite;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* Fix visibility for buttons with background in Retro theme */
|
|
296
|
+
.dark .btn-premium,
|
|
297
|
+
.dark .bg-primary,
|
|
298
|
+
.dark .bg-white,
|
|
299
|
+
.dark .bg-primary > *,
|
|
300
|
+
.dark .bg-white > * {
|
|
301
|
+
color: #000000 !important;
|
|
302
|
+
text-shadow: none !important;
|
|
303
|
+
}
|
|
304
|
+
<% end %>
|
|
187
305
|
</style>
|
|
188
306
|
</head>
|
|
189
|
-
<body class="<%=
|
|
307
|
+
<body class="<%= ['dark_glass', 'cyber_sidebar', 'retro_terminal'].include?(FEATURES_CONFIG['theme']) ? 'dark' : '' %>">
|
|
190
308
|
|
|
191
309
|
<!-- Animated Background Shapes -->
|
|
192
310
|
<div class="fixed inset-0 -z-10 overflow-hidden opacity-30 pointer-events-none">
|
|
@@ -194,7 +312,66 @@
|
|
|
194
312
|
<div class="absolute top-1/2 -right-24 w-80 h-80 bg-secondary rounded-full blur-[100px] delay-700 animate-bounce"></div>
|
|
195
313
|
</div>
|
|
196
314
|
|
|
197
|
-
|
|
315
|
+
<% if ['cyber_sidebar', 'light_sidebar'].include?(FEATURES_CONFIG['theme']) %>
|
|
316
|
+
<!-- Sidebar Layout -->
|
|
317
|
+
<div class="flex min-h-screen">
|
|
318
|
+
<!-- Sidebar -->
|
|
319
|
+
<aside class="fixed left-0 top-0 bottom-0 w-64 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'glass-panel !rounded-none border-r border-black/5' : 'bg-black border-r-4 border-primary' %> p-6 z-50 hidden md:flex flex-col gap-10">
|
|
320
|
+
<div class="flex items-center gap-3">
|
|
321
|
+
<% if FEATURES_CONFIG['theme'] == 'light_sidebar' %>
|
|
322
|
+
<div class="w-10 h-10 bg-primary text-white flex items-center justify-center font-black text-xl rounded-xl shadow-lg shadow-primary/20">OF</div>
|
|
323
|
+
<span class="text-2xl font-black tracking-tighter text-slate-800">SYSTEM</span>
|
|
324
|
+
<% else %>
|
|
325
|
+
<div class="w-10 h-10 bg-primary flex items-center justify-center text-white font-black text-xl">OF</div>
|
|
326
|
+
<span class="text-2xl font-black tracking-tighter text-white">SYSTEM</span>
|
|
327
|
+
<% end %>
|
|
328
|
+
</div>
|
|
329
|
+
|
|
330
|
+
<nav class="flex flex-col gap-2">
|
|
331
|
+
<a href="/" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path == '/' ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path == '/' ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
|
|
332
|
+
<i class="fas fa-grid-2 mr-3"></i> OVERVIEW
|
|
333
|
+
</a>
|
|
334
|
+
<% Page.where(is_nav: true, is_active: true).all.each do |p| %>
|
|
335
|
+
<a href="/<%= p.slug %>" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path == "/#{p.slug}" ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path == "/#{p.slug}" ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
|
|
336
|
+
<i class="fas fa-file-code mr-3"></i> <%= p.title.upcase %>
|
|
337
|
+
</a>
|
|
338
|
+
<% end %>
|
|
339
|
+
<a href="/dashboard" class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? (req.path.start_with?('/dashboard') ? 'bg-primary/10 text-primary rounded-xl' : 'text-slate-600 hover:bg-primary/5 rounded-xl') : (req.path.start_with?('/dashboard') ? 'bg-primary text-white border-primary border-2 rounded-2xl' : 'border-white/10 text-white hover:border-primary border-2 rounded-2xl') %> font-bold transition-all">
|
|
340
|
+
<i class="fas fa-terminal mr-3"></i> CONSOLE
|
|
341
|
+
</a>
|
|
342
|
+
</nav>
|
|
343
|
+
|
|
344
|
+
<div class="mt-auto space-y-4">
|
|
345
|
+
<div class="p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'bg-slate-100/50 border border-slate-200 text-slate-500' : 'bg-white/5 border border-white/10 text-slate-400' %> text-[10px] font-mono rounded-xl">
|
|
346
|
+
<% if FEATURES_CONFIG['theme'] == 'light_sidebar' %>
|
|
347
|
+
SYSTEM: <span class="text-indigo-600 font-bold">OPERATIONAL</span><br>
|
|
348
|
+
LICENSE: <span class="text-slate-800">ENTERPRISE</span>
|
|
349
|
+
<% else %>
|
|
350
|
+
PROTOCOL: <span class="text-green-500 animate-pulse">SECURED</span><br>
|
|
351
|
+
UPLINK: <span class="text-primary">ENCRYPTED</span>
|
|
352
|
+
<% end %>
|
|
353
|
+
</div>
|
|
354
|
+
<a href="https://github.com/IshikawaUta/one-for-all-framework" class="flex items-center justify-center w-full p-4 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'bg-slate-900 text-white rounded-xl shadow-lg shadow-slate-200' : 'bg-primary text-black border-2 border-primary rounded-2xl shadow-[4px_4px_0px_var(--secondary)]' %> font-black hover:opacity-90 hover:-translate-y-0.5 transition-all">
|
|
355
|
+
<i class="fab fa-github mr-2 text-lg"></i> <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'VIEW SOURCE' : 'ACCESS REPO' %>
|
|
356
|
+
</a>
|
|
357
|
+
</div>
|
|
358
|
+
</aside>
|
|
359
|
+
|
|
360
|
+
<!-- Mobile Header for Sidebar Themes -->
|
|
361
|
+
<header class="md:hidden fixed top-0 left-0 right-0 h-16 <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'glass-panel !rounded-none border-b border-black/5' : 'bg-black border-b-4 border-primary' %> z-[60] flex items-center justify-between px-6">
|
|
362
|
+
<span class="font-black <%= FEATURES_CONFIG['theme'] == 'light_sidebar' ? 'text-slate-800' : 'text-white' %>">OFA<span class="text-primary">.SYS</span></span>
|
|
363
|
+
<button id="mobile-menu-btn" class="text-primary"><i class="fas fa-bars text-2xl"></i></button>
|
|
364
|
+
</header>
|
|
365
|
+
|
|
366
|
+
<!-- Main Content Area -->
|
|
367
|
+
<main class="flex-1 md:ml-64 p-6 md:p-12 pt-24 md:pt-12">
|
|
368
|
+
<div class="max-w-5xl mx-auto">
|
|
369
|
+
<%= @content %>
|
|
370
|
+
</div>
|
|
371
|
+
</main>
|
|
372
|
+
</div>
|
|
373
|
+
<% else %>
|
|
374
|
+
<!-- Navigation (Original Top Nav) -->
|
|
198
375
|
<header class="w-full flex justify-center p-4 sticky top-0 z-50">
|
|
199
376
|
<nav class="glass-panel w-full max-w-4xl px-8 py-4 flex items-center justify-between">
|
|
200
377
|
<div class="flex items-center gap-3">
|
|
@@ -210,15 +387,53 @@
|
|
|
210
387
|
</ul>
|
|
211
388
|
|
|
212
389
|
<div class="flex items-center gap-4">
|
|
213
|
-
<a href="https://github.com" class="text-slate-400 hover:text-primary transition-colors"><i class="fab fa-github text-xl"></i></a>
|
|
214
|
-
<button class="md:hidden text-slate-400
|
|
390
|
+
<a href="https://github.com/IshikawaUta/one-for-all-framework" class="text-slate-400 hover:text-primary transition-colors"><i class="fab fa-github text-xl"></i></a>
|
|
391
|
+
<button id="mobile-menu-btn" class="md:hidden text-slate-400 hover:text-primary transition-colors">
|
|
392
|
+
<i class="fas fa-bars text-xl"></i>
|
|
393
|
+
</button>
|
|
215
394
|
</div>
|
|
216
395
|
</nav>
|
|
217
396
|
</header>
|
|
218
397
|
|
|
398
|
+
|
|
219
399
|
<main class="container mx-auto max-w-4xl px-6 py-12">
|
|
220
400
|
<%= @content %>
|
|
221
401
|
</main>
|
|
402
|
+
<% end %>
|
|
403
|
+
|
|
404
|
+
<!-- Mobile Menu Overlay (Shared) -->
|
|
405
|
+
<div id="mobile-menu" class="fixed inset-0 z-[100] hidden">
|
|
406
|
+
<div id="mobile-menu-backdrop" class="absolute inset-0 bg-black/40 backdrop-blur-sm opacity-0"></div>
|
|
407
|
+
<div id="mobile-menu-content" class="absolute right-0 top-0 bottom-0 w-72 glass-panel !rounded-none border-l <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'border-black/5' : 'border-white/10' %> p-8 flex flex-col translate-x-full">
|
|
408
|
+
<div class="flex justify-between items-center mb-10">
|
|
409
|
+
<span class="text-xl font-black tracking-tighter">OFA<span class="text-primary">.menu</span></span>
|
|
410
|
+
<button id="mobile-menu-close" class="text-slate-400 hover:text-red-400 transition-colors">
|
|
411
|
+
<i class="fas fa-times text-2xl"></i>
|
|
412
|
+
</button>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<nav class="flex flex-col gap-6">
|
|
416
|
+
<a href="/" class="text-lg font-bold <%= req.path == '/' ? 'text-primary' : 'text-slate-500' %>">
|
|
417
|
+
<i class="fas fa-home w-8"></i> Home
|
|
418
|
+
</a>
|
|
419
|
+
<% Page.where(is_nav: true, is_active: true).all.each do |p| %>
|
|
420
|
+
<a href="/<%= p.slug %>" class="text-lg font-bold <%= req.path == "/#{p.slug}" ? 'text-primary' : 'text-slate-500' %>">
|
|
421
|
+
<i class="fas fa-file-alt w-8"></i> <%= p.title %>
|
|
422
|
+
</a>
|
|
423
|
+
<% end %>
|
|
424
|
+
<div class="h-px <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'bg-black/10' : 'bg-white/5' %> my-2"></div>
|
|
425
|
+
<a href="/dashboard" class="text-lg font-bold <%= req.path.start_with?('/dashboard') ? 'text-primary' : (['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'text-slate-600' : 'text-slate-500') %>">
|
|
426
|
+
<i class="fas fa-gauge-high w-8"></i> Dashboard
|
|
427
|
+
</a>
|
|
428
|
+
</nav>
|
|
429
|
+
|
|
430
|
+
<div class="mt-auto pt-10 border-t <%= ['light_sidebar', 'light_glass'].include?(FEATURES_CONFIG['theme']) ? 'border-black/10' : 'border-white/5' %> flex gap-6 justify-center">
|
|
431
|
+
<a href="#" class="text-slate-400 hover:text-primary text-xl"><i class="fab fa-twitter"></i></a>
|
|
432
|
+
<a href="#" class="text-slate-400 hover:text-primary text-xl"><i class="fab fa-discord"></i></a>
|
|
433
|
+
<a href="https://github.com/IshikawaUta/one-for-all-framework" class="text-slate-400 hover:text-primary text-xl"><i class="fab fa-github"></i></a>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</div>
|
|
222
437
|
|
|
223
438
|
<!-- Footer -->
|
|
224
439
|
<footer class="w-full py-12 mt-20 border-t border-white/5 text-center text-slate-500 text-sm">
|
|
@@ -244,6 +459,51 @@
|
|
|
244
459
|
duration: 1000,
|
|
245
460
|
easing: 'easeOutCubic'
|
|
246
461
|
});
|
|
462
|
+
|
|
463
|
+
// Mobile Menu Logic
|
|
464
|
+
const menuBtn = document.getElementById('mobile-menu-btn');
|
|
465
|
+
const menuClose = document.getElementById('mobile-menu-close');
|
|
466
|
+
const menu = document.getElementById('mobile-menu');
|
|
467
|
+
const backdrop = document.getElementById('mobile-menu-backdrop');
|
|
468
|
+
const content = document.getElementById('mobile-menu-content');
|
|
469
|
+
let isMenuOpen = false;
|
|
470
|
+
|
|
471
|
+
const toggleMenu = () => {
|
|
472
|
+
isMenuOpen = !isMenuOpen;
|
|
473
|
+
if (isMenuOpen) {
|
|
474
|
+
menu.classList.remove('hidden');
|
|
475
|
+
anime({
|
|
476
|
+
targets: backdrop,
|
|
477
|
+
opacity: [0, 1],
|
|
478
|
+
duration: 400,
|
|
479
|
+
easing: 'easeOutQuad'
|
|
480
|
+
});
|
|
481
|
+
anime({
|
|
482
|
+
targets: content,
|
|
483
|
+
translateX: ['100%', '0%'],
|
|
484
|
+
duration: 600,
|
|
485
|
+
easing: 'easeOutExpo'
|
|
486
|
+
});
|
|
487
|
+
} else {
|
|
488
|
+
anime({
|
|
489
|
+
targets: backdrop,
|
|
490
|
+
opacity: 0,
|
|
491
|
+
duration: 300,
|
|
492
|
+
easing: 'easeInQuad'
|
|
493
|
+
});
|
|
494
|
+
anime({
|
|
495
|
+
targets: content,
|
|
496
|
+
translateX: '100%',
|
|
497
|
+
duration: 400,
|
|
498
|
+
easing: 'easeInExpo',
|
|
499
|
+
complete: () => menu.classList.add('hidden')
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
menuBtn.addEventListener('click', toggleMenu);
|
|
505
|
+
menuClose.addEventListener('click', toggleMenu);
|
|
506
|
+
backdrop.addEventListener('click', toggleMenu);
|
|
247
507
|
});
|
|
248
508
|
</script>
|
|
249
509
|
</body>
|
data/app/views/login.erb
CHANGED
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
<div class="text-center pt-4 border-t border-white/5">
|
|
40
40
|
<p class="text-sm text-slate-500 dark:text-slate-400">
|
|
41
|
-
Forgot password? <a href="
|
|
41
|
+
Forgot password? <a href="mailto:komikers09@gmail.com" class="text-primary font-bold hover:underline">Contact System Admin</a>
|
|
42
42
|
</p>
|
|
43
43
|
</div>
|
|
44
44
|
</div>
|
data/bin/ofa
CHANGED
|
@@ -17,6 +17,13 @@ rescue LoadError
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def help
|
|
20
|
+
puts " "
|
|
21
|
+
puts " ____ _______ ___ "
|
|
22
|
+
puts " / __ \\/ ____/ / | "
|
|
23
|
+
puts " / / / / /_ / /| | Framework "
|
|
24
|
+
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
25
|
+
puts "\\____/_/ /_/ |_| v2.0.0 "
|
|
26
|
+
puts " "
|
|
20
27
|
puts "✨ One-For-All Framework CLI ✨"
|
|
21
28
|
puts "-----------------------------"
|
|
22
29
|
puts "Usage:"
|
|
@@ -28,7 +35,7 @@ def help
|
|
|
28
35
|
puts " ofa g post TITLE - Create a new post [args: --category, --author, --image]"
|
|
29
36
|
puts " ofa feature ACTION F - Toggle features: action=enable/disable, F=cms/auth"
|
|
30
37
|
puts " ofa type NAME - Set application type: portfolio, blog, landing_page"
|
|
31
|
-
puts " ofa theme NAME - Set UI theme: light_glass, dark_glass"
|
|
38
|
+
puts " ofa theme NAME - Set UI theme: light_glass, dark_glass, cyber_sidebar, retro_terminal, light_sidebar"
|
|
32
39
|
puts " ofa storage NAME - Set image storage: local, cloudinary"
|
|
33
40
|
puts " ofa reset-password USR PWD - Reset admin account password"
|
|
34
41
|
puts " ofa db switch TYPE [NAME] - Switch DB: sqlite, mysql, mariadb, mongodb, postgres"
|
|
@@ -101,6 +108,13 @@ when 'new'
|
|
|
101
108
|
|
|
102
109
|
when 'init'
|
|
103
110
|
app_type = ARGV.shift || 'landing_page'
|
|
111
|
+
puts " "
|
|
112
|
+
puts " ____ _______ ___ "
|
|
113
|
+
puts " / __ \\/ ____/ / | "
|
|
114
|
+
puts " / / / / /_ / /| | Framework "
|
|
115
|
+
puts "/ /_/ / __/ / ___ | Premium MVC "
|
|
116
|
+
puts "\\____/_/ /_/ |_| v2.0.0 "
|
|
117
|
+
puts " "
|
|
104
118
|
puts "Initializing One-For-All project as '#{app_type}' in #{PROJECT_ROOT}..."
|
|
105
119
|
|
|
106
120
|
# --- Interactive Wizard ---
|
|
@@ -130,7 +144,7 @@ when 'init'
|
|
|
130
144
|
puts "-----------------------------\n"
|
|
131
145
|
|
|
132
146
|
# Create directories
|
|
133
|
-
dirs = ['app/controllers', 'app/models', 'app/views', 'config', 'db/migrations', 'public/css', 'public/js', 'public/
|
|
147
|
+
dirs = ['app/controllers', 'app/models', 'app/views', 'config', 'db/migrations', 'public/css', 'public/js', 'public/images', 'public/images/uploads']
|
|
134
148
|
dirs.each { |dir| FileUtils.mkdir_p(File.join(PROJECT_ROOT, dir)) }
|
|
135
149
|
|
|
136
150
|
# Copy framework core migrations
|
|
@@ -263,8 +277,6 @@ when 'init'
|
|
|
263
277
|
File.write(db_config_path, JSON.pretty_generate(db_default))
|
|
264
278
|
end
|
|
265
279
|
|
|
266
|
-
# Premium Scaffolding
|
|
267
|
-
puts " Generating premium UI components..."
|
|
268
280
|
# Copy views from framework to project
|
|
269
281
|
framework_views = File.join(FRAMEWORK_ROOT, 'app', 'views')
|
|
270
282
|
project_views = File.join(PROJECT_ROOT, 'app', 'views')
|
|
@@ -272,6 +284,14 @@ when 'init'
|
|
|
272
284
|
# Recursive copy for views (including cms folder)
|
|
273
285
|
FileUtils.cp_r(File.join(framework_views, '.'), project_views)
|
|
274
286
|
|
|
287
|
+
# Copy public assets from framework to project
|
|
288
|
+
framework_public = File.join(FRAMEWORK_ROOT, 'public')
|
|
289
|
+
project_public = File.join(PROJECT_ROOT, 'public')
|
|
290
|
+
if File.directory?(framework_public)
|
|
291
|
+
# Use cp_r with '.' to copy contents of public, not the public folder itself
|
|
292
|
+
FileUtils.cp_r(File.join(framework_public, '.'), project_public)
|
|
293
|
+
end
|
|
294
|
+
|
|
275
295
|
# Copy models from framework to project
|
|
276
296
|
framework_models = File.join(FRAMEWORK_ROOT, 'app', 'models')
|
|
277
297
|
project_models = File.join(PROJECT_ROOT, 'app', 'models')
|
|
@@ -389,8 +409,8 @@ when 'type'
|
|
|
389
409
|
when 'theme'
|
|
390
410
|
ensure_initialized!
|
|
391
411
|
name = ARGV.shift
|
|
392
|
-
unless %w[light_glass dark_glass].include?(name)
|
|
393
|
-
puts "❌ Error: Invalid theme '#{name}'. Choose: light_glass, dark_glass."
|
|
412
|
+
unless %w[light_glass dark_glass cyber_sidebar retro_terminal light_sidebar].include?(name)
|
|
413
|
+
puts "❌ Error: Invalid theme '#{name}'. Choose: light_glass, dark_glass, cyber_sidebar, retro_terminal, light_sidebar."
|
|
394
414
|
exit 1
|
|
395
415
|
end
|
|
396
416
|
config_path = File.join(PROJECT_ROOT, 'config', 'features.json')
|
data/config/features.json
CHANGED
data/db/development.sqlite3
CHANGED
|
Binary file
|