@tutorialkit-rb/cli 0.1.4
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.
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.js +1364 -0
- package/package.json +61 -0
- package/template/.gitignore +14 -0
- package/template/.vscode/extensions.json +4 -0
- package/template/.vscode/launch.json +11 -0
- package/template/README.md +179 -0
- package/template/astro.config.ts +21 -0
- package/template/bin/build-wasm +40 -0
- package/template/icons/languages/css.svg +1 -0
- package/template/icons/languages/html.svg +1 -0
- package/template/icons/languages/js.svg +1 -0
- package/template/icons/languages/json.svg +1 -0
- package/template/icons/languages/markdown.svg +1 -0
- package/template/icons/languages/ruby.svg +1 -0
- package/template/icons/languages/sass.svg +1 -0
- package/template/icons/languages/ts.svg +1 -0
- package/template/icons/phosphor/file-erb.svg +1 -0
- package/template/icons/phosphor/file-rb.svg +5 -0
- package/template/package.json +37 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/logo-dark.svg +4 -0
- package/template/public/logo.svg +4 -0
- package/template/ruby-wasm/.railsrc +12 -0
- package/template/ruby-wasm/Gemfile +22 -0
- package/template/ruby-wasm/Gemfile.lock +292 -0
- package/template/ruby-wasm/README.md +19 -0
- package/template/ruby-wasm/bin/pack +16 -0
- package/template/ruby-wasm/boot.rb +32 -0
- package/template/ruby-wasm/config/wasmify.yml +23 -0
- package/template/src/components/FileManager.tsx +165 -0
- package/template/src/components/GitHubLink.astro +17 -0
- package/template/src/components/HeadTags.astro +65 -0
- package/template/src/components/HelpDropdown.tsx +72 -0
- package/template/src/components/RailsPathLinkHandler.tsx +107 -0
- package/template/src/components/ShellConfigurator.tsx +95 -0
- package/template/src/components/TopBar.astro +48 -0
- package/template/src/content/config.ts +9 -0
- package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/_files/workspace/.keep +0 -0
- package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/content.md +34 -0
- package/template/src/content/tutorial/1-getting-started/2-rails-console/_files/.tk-config.json +3 -0
- package/template/src/content/tutorial/1-getting-started/2-rails-console/_files/workspace/.keep +0 -0
- package/template/src/content/tutorial/1-getting-started/2-rails-console/content.md +37 -0
- package/template/src/content/tutorial/1-getting-started/meta.md +4 -0
- package/template/src/content/tutorial/2-controllers/2-crud-operations/_files/.tk-config.json +3 -0
- package/template/src/content/tutorial/2-controllers/2-crud-operations/_files/workspace/.keep +0 -0
- package/template/src/content/tutorial/2-controllers/2-crud-operations/content.md +99 -0
- package/template/src/content/tutorial/2-controllers/meta.md +4 -0
- package/template/src/content/tutorial/meta.md +18 -0
- package/template/src/env.d.ts +3 -0
- package/template/src/plugins/remarkRailsPathLinks.ts +39 -0
- package/template/src/templates/crud-products/.tk-config.json +3 -0
- package/template/src/templates/crud-products/workspace/.keep +0 -0
- package/template/src/templates/crud-products/workspace/store/app/controllers/products_controller.rb +48 -0
- package/template/src/templates/crud-products/workspace/store/app/models/product.rb +3 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/_form.html.erb +10 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/edit.html.erb +4 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/index.html.erb +11 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/new.html.erb +4 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/show.html.erb +5 -0
- package/template/src/templates/crud-products/workspace/store/config/routes.rb +6 -0
- package/template/src/templates/crud-products/workspace/store/db/migrate/20250521010850_create_products.rb +9 -0
- package/template/src/templates/crud-products/workspace/store/db/schema.rb +22 -0
- package/template/src/templates/crud-products/workspace/store/db/seeds.rb +3 -0
- package/template/src/templates/crud-products/workspace/store/test/fixtures/products.yml +7 -0
- package/template/src/templates/crud-products/workspace/store/test/models/product_test.rb +7 -0
- package/template/src/templates/default/bin/console +9 -0
- package/template/src/templates/default/bin/rackup +11 -0
- package/template/src/templates/default/bin/rails +41 -0
- package/template/src/templates/default/bin/ruby +37 -0
- package/template/src/templates/default/lib/commands.js +39 -0
- package/template/src/templates/default/lib/database.js +46 -0
- package/template/src/templates/default/lib/irb.js +110 -0
- package/template/src/templates/default/lib/patches/app_generator.rb +43 -0
- package/template/src/templates/default/lib/patches/authentication.rb +24 -0
- package/template/src/templates/default/lib/rails.js +69 -0
- package/template/src/templates/default/lib/server/frame_location_middleware.js +77 -0
- package/template/src/templates/default/lib/server.js +307 -0
- package/template/src/templates/default/package-lock.json +1830 -0
- package/template/src/templates/default/package.json +23 -0
- package/template/src/templates/default/pgdata/.keep +0 -0
- package/template/src/templates/default/scripts/createdb.js +7 -0
- package/template/src/templates/default/scripts/rails.js +52 -0
- package/template/src/templates/default/scripts/wait-for-wasm.js +103 -0
- package/template/src/templates/default/workspace/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/.ruby-version +1 -0
- package/template/src/templates/rails-app/workspace/store/Gemfile +37 -0
- package/template/src/templates/rails-app/workspace/store/README.md +24 -0
- package/template/src/templates/rails-app/workspace/store/Rakefile +6 -0
- package/template/src/templates/rails-app/workspace/store/app/assets/images/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/app/assets/stylesheets/application.css +10 -0
- package/template/src/templates/rails-app/workspace/store/app/controllers/application_controller.rb +4 -0
- package/template/src/templates/rails-app/workspace/store/app/helpers/application_helper.rb +2 -0
- package/template/src/templates/rails-app/workspace/store/app/javascript/application.js +4 -0
- package/template/src/templates/rails-app/workspace/store/app/javascript/controllers/application.js +9 -0
- package/template/src/templates/rails-app/workspace/store/app/javascript/controllers/index.js +4 -0
- package/template/src/templates/rails-app/workspace/store/app/jobs/application_job.rb +7 -0
- package/template/src/templates/rails-app/workspace/store/app/mailers/application_mailer.rb +4 -0
- package/template/src/templates/rails-app/workspace/store/app/models/application_record.rb +3 -0
- package/template/src/templates/rails-app/workspace/store/app/models/concerns/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/app/views/layouts/application.html.erb +28 -0
- package/template/src/templates/rails-app/workspace/store/app/views/layouts/mailer.html.erb +13 -0
- package/template/src/templates/rails-app/workspace/store/app/views/layouts/mailer.text.erb +1 -0
- package/template/src/templates/rails-app/workspace/store/app/views/pwa/manifest.json.erb +22 -0
- package/template/src/templates/rails-app/workspace/store/app/views/pwa/service-worker.js +26 -0
- package/template/src/templates/rails-app/workspace/store/bin/importmap +4 -0
- package/template/src/templates/rails-app/workspace/store/bin/rails +4 -0
- package/template/src/templates/rails-app/workspace/store/config/application.rb +30 -0
- package/template/src/templates/rails-app/workspace/store/config/boot.rb +3 -0
- package/template/src/templates/rails-app/workspace/store/config/cable.yml +10 -0
- package/template/src/templates/rails-app/workspace/store/config/credentials.yml.enc +1 -0
- package/template/src/templates/rails-app/workspace/store/config/database.yml +32 -0
- package/template/src/templates/rails-app/workspace/store/config/environment.rb +5 -0
- package/template/src/templates/rails-app/workspace/store/config/environments/development.rb +69 -0
- package/template/src/templates/rails-app/workspace/store/config/environments/production.rb +89 -0
- package/template/src/templates/rails-app/workspace/store/config/environments/test.rb +53 -0
- package/template/src/templates/rails-app/workspace/store/config/importmap.rb +9 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/assets.rb +7 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/content_security_policy.rb +25 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/filter_parameter_logging.rb +8 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/inflections.rb +16 -0
- package/template/src/templates/rails-app/workspace/store/config/locales/en.yml +31 -0
- package/template/src/templates/rails-app/workspace/store/config/master.key +1 -0
- package/template/src/templates/rails-app/workspace/store/config/puma.rb +41 -0
- package/template/src/templates/rails-app/workspace/store/config/routes.rb +4 -0
- package/template/src/templates/rails-app/workspace/store/config/storage.yml +34 -0
- package/template/src/templates/rails-app/workspace/store/config.ru +6 -0
- package/template/src/templates/rails-app/workspace/store/db/seeds.rb +9 -0
- package/template/src/templates/rails-app/workspace/store/log/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/public/400.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/404.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/406-unsupported-browser.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/422.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/500.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/icon.png +0 -0
- package/template/src/templates/rails-app/workspace/store/public/icon.svg +3 -0
- package/template/src/templates/rails-app/workspace/store/public/robots.txt +1 -0
- package/template/src/templates/rails-app/workspace/store/script/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/storage/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/controllers/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/helpers/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/integration/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/test_helper.rb +15 -0
- package/template/src/templates/rails-app/workspace/store/tmp/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/tmp/pids/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/tmp/storage/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/vendor/javascripts/.keep +0 -0
- package/template/tsconfig.json +16 -0
- package/template/uno.config.ts +10 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
GEM
|
|
2
|
+
remote: https://rubygems.org/
|
|
3
|
+
specs:
|
|
4
|
+
actioncable (8.0.2)
|
|
5
|
+
actionpack (= 8.0.2)
|
|
6
|
+
activesupport (= 8.0.2)
|
|
7
|
+
nio4r (~> 2.0)
|
|
8
|
+
websocket-driver (>= 0.6.1)
|
|
9
|
+
zeitwerk (~> 2.6)
|
|
10
|
+
actionmailbox (8.0.2)
|
|
11
|
+
actionpack (= 8.0.2)
|
|
12
|
+
activejob (= 8.0.2)
|
|
13
|
+
activerecord (= 8.0.2)
|
|
14
|
+
activestorage (= 8.0.2)
|
|
15
|
+
activesupport (= 8.0.2)
|
|
16
|
+
mail (>= 2.8.0)
|
|
17
|
+
actionmailer (8.0.2)
|
|
18
|
+
actionpack (= 8.0.2)
|
|
19
|
+
actionview (= 8.0.2)
|
|
20
|
+
activejob (= 8.0.2)
|
|
21
|
+
activesupport (= 8.0.2)
|
|
22
|
+
mail (>= 2.8.0)
|
|
23
|
+
rails-dom-testing (~> 2.2)
|
|
24
|
+
actionpack (8.0.2)
|
|
25
|
+
actionview (= 8.0.2)
|
|
26
|
+
activesupport (= 8.0.2)
|
|
27
|
+
nokogiri (>= 1.8.5)
|
|
28
|
+
rack (>= 2.2.4)
|
|
29
|
+
rack-session (>= 1.0.1)
|
|
30
|
+
rack-test (>= 0.6.3)
|
|
31
|
+
rails-dom-testing (~> 2.2)
|
|
32
|
+
rails-html-sanitizer (~> 1.6)
|
|
33
|
+
useragent (~> 0.16)
|
|
34
|
+
actiontext (8.0.2)
|
|
35
|
+
actionpack (= 8.0.2)
|
|
36
|
+
activerecord (= 8.0.2)
|
|
37
|
+
activestorage (= 8.0.2)
|
|
38
|
+
activesupport (= 8.0.2)
|
|
39
|
+
globalid (>= 0.6.0)
|
|
40
|
+
nokogiri (>= 1.8.5)
|
|
41
|
+
actionview (8.0.2)
|
|
42
|
+
activesupport (= 8.0.2)
|
|
43
|
+
builder (~> 3.1)
|
|
44
|
+
erubi (~> 1.11)
|
|
45
|
+
rails-dom-testing (~> 2.2)
|
|
46
|
+
rails-html-sanitizer (~> 1.6)
|
|
47
|
+
activejob (8.0.2)
|
|
48
|
+
activesupport (= 8.0.2)
|
|
49
|
+
globalid (>= 0.3.6)
|
|
50
|
+
activemodel (8.0.2)
|
|
51
|
+
activesupport (= 8.0.2)
|
|
52
|
+
activerecord (8.0.2)
|
|
53
|
+
activemodel (= 8.0.2)
|
|
54
|
+
activesupport (= 8.0.2)
|
|
55
|
+
timeout (>= 0.4.0)
|
|
56
|
+
activestorage (8.0.2)
|
|
57
|
+
actionpack (= 8.0.2)
|
|
58
|
+
activejob (= 8.0.2)
|
|
59
|
+
activerecord (= 8.0.2)
|
|
60
|
+
activesupport (= 8.0.2)
|
|
61
|
+
marcel (~> 1.0)
|
|
62
|
+
activesupport (8.0.2)
|
|
63
|
+
base64
|
|
64
|
+
benchmark (>= 0.3)
|
|
65
|
+
bigdecimal
|
|
66
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
67
|
+
connection_pool (>= 2.2.5)
|
|
68
|
+
drb
|
|
69
|
+
i18n (>= 1.6, < 2)
|
|
70
|
+
logger (>= 1.4.2)
|
|
71
|
+
minitest (>= 5.1)
|
|
72
|
+
securerandom (>= 0.3)
|
|
73
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
74
|
+
uri (>= 0.13.1)
|
|
75
|
+
base64 (0.2.0)
|
|
76
|
+
bcrypt (3.1.20)
|
|
77
|
+
benchmark (0.4.0)
|
|
78
|
+
bigdecimal (3.1.9)
|
|
79
|
+
builder (3.3.0)
|
|
80
|
+
concurrent-ruby (1.3.5)
|
|
81
|
+
connection_pool (2.5.3)
|
|
82
|
+
crass (1.0.6)
|
|
83
|
+
date (3.4.1)
|
|
84
|
+
drb (2.2.1)
|
|
85
|
+
erubi (1.13.1)
|
|
86
|
+
et-orbi (1.2.11)
|
|
87
|
+
tzinfo
|
|
88
|
+
ffi (1.17.1-aarch64-linux-gnu)
|
|
89
|
+
ffi (1.17.1-aarch64-linux-musl)
|
|
90
|
+
ffi (1.17.1-arm-linux-gnu)
|
|
91
|
+
ffi (1.17.1-arm-linux-musl)
|
|
92
|
+
ffi (1.17.1-arm64-darwin)
|
|
93
|
+
ffi (1.17.1-x86_64-darwin)
|
|
94
|
+
ffi (1.17.1-x86_64-linux-gnu)
|
|
95
|
+
ffi (1.17.1-x86_64-linux-musl)
|
|
96
|
+
fugit (1.11.1)
|
|
97
|
+
et-orbi (~> 1, >= 1.2.11)
|
|
98
|
+
raabro (~> 1.4)
|
|
99
|
+
globalid (1.2.1)
|
|
100
|
+
activesupport (>= 6.1)
|
|
101
|
+
i18n (1.14.7)
|
|
102
|
+
concurrent-ruby (~> 1.0)
|
|
103
|
+
image_processing (1.13.0)
|
|
104
|
+
mini_magick (>= 4.9.5, < 5)
|
|
105
|
+
ruby-vips (>= 2.0.17, < 3)
|
|
106
|
+
importmap-rails (2.1.0)
|
|
107
|
+
actionpack (>= 6.0.0)
|
|
108
|
+
activesupport (>= 6.0.0)
|
|
109
|
+
railties (>= 6.0.0)
|
|
110
|
+
io-console (0.8.0)
|
|
111
|
+
irb (1.15.2)
|
|
112
|
+
pp (>= 0.6.0)
|
|
113
|
+
rdoc (>= 4.0.0)
|
|
114
|
+
reline (>= 0.4.2)
|
|
115
|
+
jbuilder (2.13.0)
|
|
116
|
+
actionview (>= 5.0.0)
|
|
117
|
+
activesupport (>= 5.0.0)
|
|
118
|
+
js (2.7.1)
|
|
119
|
+
logger (1.7.0)
|
|
120
|
+
loofah (2.24.0)
|
|
121
|
+
crass (~> 1.0.2)
|
|
122
|
+
nokogiri (>= 1.12.0)
|
|
123
|
+
mail (2.8.1)
|
|
124
|
+
mini_mime (>= 0.1.1)
|
|
125
|
+
net-imap
|
|
126
|
+
net-pop
|
|
127
|
+
net-smtp
|
|
128
|
+
marcel (1.0.4)
|
|
129
|
+
mini_magick (4.13.2)
|
|
130
|
+
mini_mime (1.1.5)
|
|
131
|
+
minitest (5.25.5)
|
|
132
|
+
net-imap (0.5.8)
|
|
133
|
+
date
|
|
134
|
+
net-protocol
|
|
135
|
+
net-pop (0.1.2)
|
|
136
|
+
net-protocol
|
|
137
|
+
net-protocol (0.2.2)
|
|
138
|
+
timeout
|
|
139
|
+
net-smtp (0.5.1)
|
|
140
|
+
net-protocol
|
|
141
|
+
nio4r (2.7.4)
|
|
142
|
+
nokogiri (1.18.8-aarch64-linux-gnu)
|
|
143
|
+
racc (~> 1.4)
|
|
144
|
+
nokogiri (1.18.8-aarch64-linux-musl)
|
|
145
|
+
racc (~> 1.4)
|
|
146
|
+
nokogiri (1.18.8-arm-linux-gnu)
|
|
147
|
+
racc (~> 1.4)
|
|
148
|
+
nokogiri (1.18.8-arm-linux-musl)
|
|
149
|
+
racc (~> 1.4)
|
|
150
|
+
nokogiri (1.18.8-arm64-darwin)
|
|
151
|
+
racc (~> 1.4)
|
|
152
|
+
nokogiri (1.18.8-x86_64-darwin)
|
|
153
|
+
racc (~> 1.4)
|
|
154
|
+
nokogiri (1.18.8-x86_64-linux-gnu)
|
|
155
|
+
racc (~> 1.4)
|
|
156
|
+
nokogiri (1.18.8-x86_64-linux-musl)
|
|
157
|
+
racc (~> 1.4)
|
|
158
|
+
pp (0.6.2)
|
|
159
|
+
prettyprint
|
|
160
|
+
prettyprint (0.2.0)
|
|
161
|
+
propshaft (1.1.0)
|
|
162
|
+
actionpack (>= 7.0.0)
|
|
163
|
+
activesupport (>= 7.0.0)
|
|
164
|
+
rack
|
|
165
|
+
railties (>= 7.0.0)
|
|
166
|
+
psych (5.2.4)
|
|
167
|
+
date
|
|
168
|
+
stringio
|
|
169
|
+
raabro (1.4.0)
|
|
170
|
+
racc (1.8.1)
|
|
171
|
+
rack (3.1.13)
|
|
172
|
+
rack-session (2.1.1)
|
|
173
|
+
base64 (>= 0.1.0)
|
|
174
|
+
rack (>= 3.0.0)
|
|
175
|
+
rack-test (2.2.0)
|
|
176
|
+
rack (>= 1.3)
|
|
177
|
+
rackup (2.2.1)
|
|
178
|
+
rack (>= 3)
|
|
179
|
+
rails (8.0.2)
|
|
180
|
+
actioncable (= 8.0.2)
|
|
181
|
+
actionmailbox (= 8.0.2)
|
|
182
|
+
actionmailer (= 8.0.2)
|
|
183
|
+
actionpack (= 8.0.2)
|
|
184
|
+
actiontext (= 8.0.2)
|
|
185
|
+
actionview (= 8.0.2)
|
|
186
|
+
activejob (= 8.0.2)
|
|
187
|
+
activemodel (= 8.0.2)
|
|
188
|
+
activerecord (= 8.0.2)
|
|
189
|
+
activestorage (= 8.0.2)
|
|
190
|
+
activesupport (= 8.0.2)
|
|
191
|
+
bundler (>= 1.15.0)
|
|
192
|
+
railties (= 8.0.2)
|
|
193
|
+
rails-dom-testing (2.2.0)
|
|
194
|
+
activesupport (>= 5.0.0)
|
|
195
|
+
minitest
|
|
196
|
+
nokogiri (>= 1.6)
|
|
197
|
+
rails-html-sanitizer (1.6.2)
|
|
198
|
+
loofah (~> 2.21)
|
|
199
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
|
200
|
+
railties (8.0.2)
|
|
201
|
+
actionpack (= 8.0.2)
|
|
202
|
+
activesupport (= 8.0.2)
|
|
203
|
+
irb (~> 1.13)
|
|
204
|
+
rackup (>= 1.0.0)
|
|
205
|
+
rake (>= 12.2)
|
|
206
|
+
thor (~> 1.0, >= 1.2.2)
|
|
207
|
+
zeitwerk (~> 2.6)
|
|
208
|
+
rake (13.2.1)
|
|
209
|
+
rdoc (6.13.1)
|
|
210
|
+
psych (>= 4.0.0)
|
|
211
|
+
reline (0.6.1)
|
|
212
|
+
io-console (~> 0.5)
|
|
213
|
+
ruby-vips (2.2.2)
|
|
214
|
+
ffi (~> 1.12)
|
|
215
|
+
logger
|
|
216
|
+
ruby_wasm (2.7.1)
|
|
217
|
+
ruby_wasm (2.7.1-aarch64-linux)
|
|
218
|
+
ruby_wasm (2.7.1-aarch64-linux-musl)
|
|
219
|
+
ruby_wasm (2.7.1-arm64-darwin)
|
|
220
|
+
ruby_wasm (2.7.1-x86_64-darwin)
|
|
221
|
+
ruby_wasm (2.7.1-x86_64-linux)
|
|
222
|
+
ruby_wasm (2.7.1-x86_64-linux-musl)
|
|
223
|
+
securerandom (0.4.1)
|
|
224
|
+
solid_cable (3.0.4)
|
|
225
|
+
actioncable (>= 7.2)
|
|
226
|
+
activejob (>= 7.2)
|
|
227
|
+
activerecord (>= 7.2)
|
|
228
|
+
railties (>= 7.2)
|
|
229
|
+
solid_cache (1.0.6)
|
|
230
|
+
activejob (>= 7.2)
|
|
231
|
+
activerecord (>= 7.2)
|
|
232
|
+
railties (>= 7.2)
|
|
233
|
+
solid_queue (1.1.0)
|
|
234
|
+
activejob (>= 7.1)
|
|
235
|
+
activerecord (>= 7.1)
|
|
236
|
+
concurrent-ruby (>= 1.3.1)
|
|
237
|
+
fugit (~> 1.11.0)
|
|
238
|
+
railties (>= 7.1)
|
|
239
|
+
thor (~> 1.3.1)
|
|
240
|
+
stimulus-rails (1.3.4)
|
|
241
|
+
railties (>= 6.0.0)
|
|
242
|
+
stringio (3.1.7)
|
|
243
|
+
thor (1.3.2)
|
|
244
|
+
timeout (0.4.3)
|
|
245
|
+
turbo-rails (2.0.11)
|
|
246
|
+
actionpack (>= 6.0.0)
|
|
247
|
+
railties (>= 6.0.0)
|
|
248
|
+
tzinfo (2.0.6)
|
|
249
|
+
concurrent-ruby (~> 1.0)
|
|
250
|
+
tzinfo-data (1.2025.2)
|
|
251
|
+
tzinfo (>= 1.0.0)
|
|
252
|
+
uri (1.0.3)
|
|
253
|
+
useragent (0.16.11)
|
|
254
|
+
wasmify-rails (0.4.0)
|
|
255
|
+
js (>= 2.6, < 3.0)
|
|
256
|
+
railties (>= 7.0, < 9.0)
|
|
257
|
+
ruby_wasm (>= 2.6, < 3.0)
|
|
258
|
+
websocket-driver (0.7.7)
|
|
259
|
+
base64
|
|
260
|
+
websocket-extensions (>= 0.1.0)
|
|
261
|
+
websocket-extensions (0.1.5)
|
|
262
|
+
zeitwerk (2.7.2)
|
|
263
|
+
|
|
264
|
+
PLATFORMS
|
|
265
|
+
aarch64-linux
|
|
266
|
+
aarch64-linux-gnu
|
|
267
|
+
aarch64-linux-musl
|
|
268
|
+
arm-linux-gnu
|
|
269
|
+
arm-linux-musl
|
|
270
|
+
arm64-darwin
|
|
271
|
+
x86_64-darwin
|
|
272
|
+
x86_64-linux
|
|
273
|
+
x86_64-linux-gnu
|
|
274
|
+
x86_64-linux-musl
|
|
275
|
+
|
|
276
|
+
DEPENDENCIES
|
|
277
|
+
bcrypt (~> 3.1.7)
|
|
278
|
+
image_processing (~> 1.2)
|
|
279
|
+
importmap-rails
|
|
280
|
+
jbuilder
|
|
281
|
+
propshaft
|
|
282
|
+
rails (~> 8.0.0)
|
|
283
|
+
solid_cable
|
|
284
|
+
solid_cache
|
|
285
|
+
solid_queue
|
|
286
|
+
stimulus-rails
|
|
287
|
+
turbo-rails
|
|
288
|
+
tzinfo-data
|
|
289
|
+
wasmify-rails (~> 0.4.0)
|
|
290
|
+
|
|
291
|
+
BUNDLED WITH
|
|
292
|
+
2.6.5
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
To create a `ruby.wasm` module, run the following commands:
|
|
2
|
+
|
|
3
|
+
```sh
|
|
4
|
+
bundle install
|
|
5
|
+
bin/pack
|
|
6
|
+
```
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
To build and pack Ruby Wasm modules, you need the following:
|
|
12
|
+
|
|
13
|
+
- Rust toolchain:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- [wasi-vfs](https://github.com/kateinoigakukun/wasi-vfs)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require "wasmify/rails/builder"
|
|
7
|
+
|
|
8
|
+
builder = Wasmify::Rails::Builder.new
|
|
9
|
+
builder.run(name: "ruby.wasm")
|
|
10
|
+
|
|
11
|
+
require "wasmify/rails/packer"
|
|
12
|
+
|
|
13
|
+
output_dir = ARGV[0] ? ARGV[0] : Wasmify::Rails.config.output_dir
|
|
14
|
+
|
|
15
|
+
packer = Wasmify::Rails::Packer.new(output_dir:)
|
|
16
|
+
packer.run(name: "ruby.wasm", ruby_wasm_path: File.join(Wasmify::Rails.config.tmp_dir, "ruby.wasm"))
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Put all the code required to initialize the Rails Wasm environment
|
|
2
|
+
|
|
3
|
+
# Common Rails shims
|
|
4
|
+
require "wasmify/rails/shim"
|
|
5
|
+
|
|
6
|
+
# Load Rails patches
|
|
7
|
+
require "wasmify/rails/patches"
|
|
8
|
+
|
|
9
|
+
# Setup external commands
|
|
10
|
+
require "wasmify/external_commands"
|
|
11
|
+
|
|
12
|
+
# Patch Bundler.require to only require precompiled deps
|
|
13
|
+
# (We don't want to deal with group: :wasm here)
|
|
14
|
+
def Bundler.require(*groups)
|
|
15
|
+
%w[
|
|
16
|
+
rails
|
|
17
|
+
wasmify-rails
|
|
18
|
+
propshaft
|
|
19
|
+
importmap-rails
|
|
20
|
+
turbo-rails
|
|
21
|
+
stimulus-rails
|
|
22
|
+
jbuilder
|
|
23
|
+
bcrypt
|
|
24
|
+
solid_cache
|
|
25
|
+
solid_queue
|
|
26
|
+
solid_cable
|
|
27
|
+
image_processing
|
|
28
|
+
tzinfo/data
|
|
29
|
+
].each do |gem_name|
|
|
30
|
+
Kernel.require gem_name
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
output_dir: "./dist"
|
|
2
|
+
ruby_version: "3.3"
|
|
3
|
+
|
|
4
|
+
# Specify the list of directories to be included into the final
|
|
5
|
+
# app.wasm file.
|
|
6
|
+
pack_directories: []
|
|
7
|
+
pack_root: "/rails-vm"
|
|
8
|
+
additional_root_files:
|
|
9
|
+
- boot.rb
|
|
10
|
+
- .railsrc
|
|
11
|
+
|
|
12
|
+
# Specify the list of gems to skip during the base module compiliation.
|
|
13
|
+
# Usually, you want to specify the gems with native extensions that are
|
|
14
|
+
# not currently Wasm-compatible.
|
|
15
|
+
exclude_gems:
|
|
16
|
+
- nio4r
|
|
17
|
+
- io-console
|
|
18
|
+
- psych
|
|
19
|
+
|
|
20
|
+
# Skip building native extensions for the following gems (and keep their Ruby source)
|
|
21
|
+
ignore_gem_extensions:
|
|
22
|
+
- bigdecimal
|
|
23
|
+
- date
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useStore } from '@nanostores/react';
|
|
2
|
+
import type { WebContainer } from '@webcontainer/api';
|
|
3
|
+
import { useRef, useEffect } from 'react';
|
|
4
|
+
import { webcontainer } from 'tutorialkit:core';
|
|
5
|
+
import tutorialStore from 'tutorialkit:store';
|
|
6
|
+
|
|
7
|
+
const VERSIONED_WASM_URL = `/ruby.wasm`;
|
|
8
|
+
const GEMFILE_HASH_URL = `/ruby.wasm.hash`;
|
|
9
|
+
const WC_WASM_LOG_PATH = `/ruby.wasm.log.txt`;
|
|
10
|
+
const WC_WASM_PATH = `/ruby.wasm`;
|
|
11
|
+
|
|
12
|
+
export function FileManager() {
|
|
13
|
+
const lessonLoaded = useStore(tutorialStore.lessonFullyLoaded);
|
|
14
|
+
const files = useStore(tutorialStore.files);
|
|
15
|
+
const processedFiles = useRef(new Set<string>());
|
|
16
|
+
const wasmCached = useRef(false);
|
|
17
|
+
|
|
18
|
+
async function fetchGemfileHash(): Promise<string> {
|
|
19
|
+
try {
|
|
20
|
+
console.log(`Fetching Gemfile hash from ${GEMFILE_HASH_URL}...`);
|
|
21
|
+
|
|
22
|
+
const response = await fetch(GEMFILE_HASH_URL);
|
|
23
|
+
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
console.warn(`Failed to fetch ruby.wasm.hash: ${response.status}`);
|
|
26
|
+
return 'default';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const hash = (await response.text()).trim();
|
|
30
|
+
console.log(`Fetched Gemfile hash: ${hash}`);
|
|
31
|
+
|
|
32
|
+
return hash;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn('Failed to fetch Gemfile hash, using default version:', error);
|
|
35
|
+
return 'default';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function getVersionedCacheFileName(gemfileLockHash: string): string {
|
|
40
|
+
return `ruby-${gemfileLockHash}.wasm`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function chmodx(wc: WebContainer, path: string) {
|
|
44
|
+
const process = await wc.spawn('chmod', ['+x', path]);
|
|
45
|
+
|
|
46
|
+
const exitCode = await process.exit;
|
|
47
|
+
|
|
48
|
+
if (exitCode !== 0) {
|
|
49
|
+
console.error(`failed to chmox +x ${path}: `, exitCode);
|
|
50
|
+
} else {
|
|
51
|
+
console.log(`updated permissions for: ${path}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function fetchCachedWasmFile(cacheFileName: string): Promise<Uint8Array | null> {
|
|
56
|
+
try {
|
|
57
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
58
|
+
const fileHandle = await opfsRoot.getFileHandle(cacheFileName);
|
|
59
|
+
const file = await fileHandle.getFile();
|
|
60
|
+
console.log(`Found cached Ruby WASM: ${cacheFileName}`);
|
|
61
|
+
|
|
62
|
+
return new Uint8Array(await file.arrayBuffer());
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function persistWasmFile(wasmData: Uint8Array, cacheFileName: string): Promise<void> {
|
|
69
|
+
try {
|
|
70
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
71
|
+
const fileHandle = await opfsRoot.getFileHandle(cacheFileName, { create: true });
|
|
72
|
+
const writable = await fileHandle.createWritable();
|
|
73
|
+
await writable.write(wasmData);
|
|
74
|
+
await writable.close();
|
|
75
|
+
console.log(`Ruby WASM file ${cacheFileName} cached`);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error('Failed to persist Ruby WASM:', error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function cleanupOldCacheFiles(currentCacheFileName: string): Promise<void> {
|
|
82
|
+
try {
|
|
83
|
+
const opfsRoot = await navigator.storage.getDirectory();
|
|
84
|
+
|
|
85
|
+
for await (const [name] of opfsRoot.entries()) {
|
|
86
|
+
if (
|
|
87
|
+
((name.startsWith('ruby-') && name.endsWith('.wasm')) || name === 'ruby.wasm') &&
|
|
88
|
+
name !== currentCacheFileName
|
|
89
|
+
) {
|
|
90
|
+
console.log(`Removing old cached Ruby WASM: ${name}`);
|
|
91
|
+
await opfsRoot.removeEntry(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.warn('Failed to cleanup old cache files:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function cacheWasmFile(wc: WebContainer, cacheFileName: string): Promise<void> {
|
|
100
|
+
console.log(`Dowloading WASM file ${VERSIONED_WASM_URL}...`);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const wasm = await fetch(VERSIONED_WASM_URL);
|
|
104
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: downloaded');
|
|
105
|
+
|
|
106
|
+
const wasmData = new Uint8Array(await wasm.arrayBuffer());
|
|
107
|
+
await persistWasmFile(wasmData, cacheFileName);
|
|
108
|
+
await cleanupOldCacheFiles(cacheFileName);
|
|
109
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: cached');
|
|
110
|
+
await wc.fs.writeFile(WC_WASM_PATH, wasmData);
|
|
111
|
+
} catch {
|
|
112
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: error');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!lessonLoaded) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!files) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
(async () => {
|
|
126
|
+
const wc = await webcontainer;
|
|
127
|
+
|
|
128
|
+
Object.entries(files).forEach(([_, fd]) => {
|
|
129
|
+
const dir = fd.path.split('/').filter(Boolean).slice(-2, -1)[0];
|
|
130
|
+
|
|
131
|
+
if (dir === 'bin' && !processedFiles.current.has(fd.path)) {
|
|
132
|
+
processedFiles.current = new Set([...processedFiles.current, fd.path]);
|
|
133
|
+
chmodx(wc, '/home/tutorial' + fd.path);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
if (!wasmCached.current) {
|
|
138
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: init');
|
|
139
|
+
|
|
140
|
+
const gemfileLockHash = await fetchGemfileHash();
|
|
141
|
+
const cacheFileName = getVersionedCacheFileName(gemfileLockHash);
|
|
142
|
+
console.log(`Using cache file: ${cacheFileName} (hash: ${gemfileLockHash})`);
|
|
143
|
+
|
|
144
|
+
const cachedWasm = await fetchCachedWasmFile(cacheFileName);
|
|
145
|
+
|
|
146
|
+
if (cachedWasm) {
|
|
147
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: load from cache');
|
|
148
|
+
await wc.fs.writeFile(WC_WASM_PATH, cachedWasm);
|
|
149
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: done');
|
|
150
|
+
console.log(`Ruby WASM ${cacheFileName} loaded from cache`);
|
|
151
|
+
wasmCached.current = true;
|
|
152
|
+
} else {
|
|
153
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: download');
|
|
154
|
+
await cacheWasmFile(wc, cacheFileName);
|
|
155
|
+
await wc.fs.writeFile(WC_WASM_LOG_PATH, 'status: done');
|
|
156
|
+
wasmCached.current = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
})();
|
|
160
|
+
|
|
161
|
+
return () => {};
|
|
162
|
+
}, [lessonLoaded, files]);
|
|
163
|
+
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
<a
|
|
6
|
+
href="https://github.com/evilmartians/rails-tutorial"
|
|
7
|
+
target="_blank"
|
|
8
|
+
rel="noopener noreferrer"
|
|
9
|
+
class="flex items-center font-size-3.5 text-tk-elements-topBar-iconButton-iconColor hover:text-tk-elements-topBar-iconButton-iconColorHover transition-theme bg-tk-elements-topBar-iconButton-backgroundColor hover:bg-tk-elements-topBar-iconButton-backgroundColorHover p-1 rounded-md"
|
|
10
|
+
>
|
|
11
|
+
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
|
12
|
+
<path
|
|
13
|
+
fill-rule="evenodd"
|
|
14
|
+
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
|
15
|
+
clip-rule="evenodd"></path>
|
|
16
|
+
</svg>
|
|
17
|
+
</a>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<slot name="title" />
|
|
2
|
+
<slot name="links" />
|
|
3
|
+
<slot name="meta" />
|
|
4
|
+
|
|
5
|
+
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />
|
|
6
|
+
|
|
7
|
+
<style>
|
|
8
|
+
/* Rails path links styling */
|
|
9
|
+
:global(.rails-path-link) {
|
|
10
|
+
text-decoration: none;
|
|
11
|
+
position: relative;
|
|
12
|
+
display: inline-block;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
border-radius: 0.25rem;
|
|
15
|
+
transition: all 0.2s ease;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
:global(.rails-path-link code) {
|
|
19
|
+
color: #0969da;
|
|
20
|
+
background-color: #f6f8fa;
|
|
21
|
+
padding: 0.2em 0.4em;
|
|
22
|
+
border-radius: 0.25rem;
|
|
23
|
+
font-size: 0.875em;
|
|
24
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
25
|
+
transition: all 0.2s ease;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* Dark mode support */
|
|
29
|
+
:global(.dark .rails-path-link code) {
|
|
30
|
+
color: #58a6ff;
|
|
31
|
+
background-color: #161b22;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Hover state */
|
|
35
|
+
:global(.rails-path-link:hover code) {
|
|
36
|
+
color: #0860ca;
|
|
37
|
+
background-color: #e7f1ff;
|
|
38
|
+
text-decoration: underline;
|
|
39
|
+
text-underline-offset: 0.2em;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:global(.dark .rails-path-link:hover code) {
|
|
43
|
+
color: #79c0ff;
|
|
44
|
+
background-color: #1f2937;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Active/clicked state */
|
|
48
|
+
:global(.rails-path-link:active code) {
|
|
49
|
+
transform: scale(0.98);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Focus state for accessibility */
|
|
53
|
+
:global(.rails-path-link:focus) {
|
|
54
|
+
outline: none;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:global(.rails-path-link:focus-visible code) {
|
|
58
|
+
outline: 2px solid #0969da;
|
|
59
|
+
outline-offset: 2px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
:global(.dark .rails-path-link:focus-visible code) {
|
|
63
|
+
outline-color: #58a6ff;
|
|
64
|
+
}
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export function HelpDropdown() {
|
|
4
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
5
|
+
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
function handleClickOutside(event: MouseEvent) {
|
|
9
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
|
|
10
|
+
setIsOpen(false);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
15
|
+
|
|
16
|
+
return () => {
|
|
17
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
18
|
+
};
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
const handleReload = () => {
|
|
22
|
+
window.location.reload();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<div className="relative" ref={dropdownRef}>
|
|
27
|
+
<button
|
|
28
|
+
onClick={() => setIsOpen(!isOpen)}
|
|
29
|
+
className="flex items-center font-size-3.5 text-tk-elements-topBar-iconButton-iconColor hover:text-tk-elements-topBar-iconButton-iconColorHover transition-theme bg-tk-elements-topBar-iconButton-backgroundColor hover:bg-tk-elements-topBar-iconButton-backgroundColorHover p-1 rounded-md"
|
|
30
|
+
aria-label="Help"
|
|
31
|
+
>
|
|
32
|
+
<svg
|
|
33
|
+
width="16"
|
|
34
|
+
height="16"
|
|
35
|
+
viewBox="0 0 24 24"
|
|
36
|
+
fill="none"
|
|
37
|
+
stroke="currentColor"
|
|
38
|
+
strokeWidth="2"
|
|
39
|
+
strokeLinecap="round"
|
|
40
|
+
strokeLinejoin="round"
|
|
41
|
+
className="mr-1"
|
|
42
|
+
>
|
|
43
|
+
<circle cx="12" cy="12" r="10" />
|
|
44
|
+
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
|
|
45
|
+
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
46
|
+
</svg>
|
|
47
|
+
Help
|
|
48
|
+
</button>
|
|
49
|
+
|
|
50
|
+
{isOpen && (
|
|
51
|
+
<div className="absolute right-0 mt-2 w-80 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-60">
|
|
52
|
+
<div className="p-4">
|
|
53
|
+
<div className="mb-4">
|
|
54
|
+
<p className="text-sm text-gray-700 dark:text-gray-300 mb-2">
|
|
55
|
+
Something went wrong? Simply reload the page to load the fresh contents of the lesson and continue!
|
|
56
|
+
</p>
|
|
57
|
+
<p className="text-xs text-amber-600 dark:text-amber-400 font-medium">
|
|
58
|
+
Warning: you'll start from the fresh state for this lesson, your custom edits will be lost.
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
<button
|
|
62
|
+
onClick={handleReload}
|
|
63
|
+
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2 px-4 rounded transition-colors"
|
|
64
|
+
>
|
|
65
|
+
Reload
|
|
66
|
+
</button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|