rails_devtools 0.1.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/README.md +92 -0
- data/Rakefile +8 -0
- data/app/assets/config/devtools_manifest.js +1 -0
- data/app/assets/stylesheets/devtools/application.css +15 -0
- data/app/controllers/rails_devtools/application_controller.rb +4 -0
- data/app/controllers/rails_devtools/base_controller.rb +7 -0
- data/app/controllers/rails_devtools/database_tables_controller.rb +16 -0
- data/app/controllers/rails_devtools/frontend/modules_controller.rb +18 -0
- data/app/controllers/rails_devtools/gems_controller.rb +16 -0
- data/app/controllers/rails_devtools/host_app_images_controller.rb +35 -0
- data/app/controllers/rails_devtools/image_assets_controller.rb +43 -0
- data/app/controllers/rails_devtools/routes/route_path_inputs_controller.rb +23 -0
- data/app/controllers/rails_devtools/routes_controller.rb +21 -0
- data/app/forms/rails_devtools/database_table_search_form.rb +51 -0
- data/app/forms/rails_devtools/gem_search_form.rb +86 -0
- data/app/forms/rails_devtools/image_search_form.rb +30 -0
- data/app/forms/rails_devtools/route_search_form.rb +17 -0
- data/app/helpers/rails_devtools/application_helper.rb +4 -0
- data/app/javascript/application.js +4 -0
- data/app/javascript/controllers/application.js +10 -0
- data/app/javascript/controllers/checkbox_controller.js +9 -0
- data/app/javascript/controllers/index.js +11 -0
- data/app/javascript/controllers/search_reset_controller.js +7 -0
- data/app/javascript/controllers/turbo_form_controller.js +9 -0
- data/app/jobs/rails_devtools/application_job.rb +4 -0
- data/app/mailers/rails_devtools/application_mailer.rb +6 -0
- data/app/models/rails_devtools/application_record.rb +5 -0
- data/app/models/rails_devtools/image_assets/image_info.rb +85 -0
- data/app/models/rails_devtools/routes/collection.rb +83 -0
- data/app/models/rails_devtools/routes/controller_info.rb +30 -0
- data/app/models/rails_devtools/routes/engine_info.rb +33 -0
- data/app/models/rails_devtools/routes/route_info.rb +120 -0
- data/app/views/rails_devtools/application_layout.rb +90 -0
- data/app/views/rails_devtools/application_view.rb +6 -0
- data/app/views/rails_devtools/components/application_component.rb +24 -0
- data/app/views/rails_devtools/components/flash_message.rb +29 -0
- data/app/views/rails_devtools/components/lucide/base.rb +18 -0
- data/app/views/rails_devtools/components/lucide/close.rb +25 -0
- data/app/views/rails_devtools/components/lucide/database.rb +26 -0
- data/app/views/rails_devtools/components/lucide/external_link.rb +26 -0
- data/app/views/rails_devtools/components/lucide/images.rb +27 -0
- data/app/views/rails_devtools/components/lucide/menu.rb +26 -0
- data/app/views/rails_devtools/components/lucide/package.rb +30 -0
- data/app/views/rails_devtools/components/lucide/pocket_knife.rb +28 -0
- data/app/views/rails_devtools/components/lucide/sign_post.rb +29 -0
- data/app/views/rails_devtools/components/lucide/trash.rb +26 -0
- data/app/views/rails_devtools/components/lucide/triangle_alert.rb +29 -0
- data/app/views/rails_devtools/components/page_content.rb +27 -0
- data/app/views/rails_devtools/components/ui/drawer.rb +49 -0
- data/app/views/rails_devtools/components/ui/menu.rb +81 -0
- data/app/views/rails_devtools/components/ui/search_form.rb +65 -0
- data/app/views/rails_devtools/components.rb +5 -0
- data/app/views/rails_devtools/database_tables/index.rb +32 -0
- data/app/views/rails_devtools/database_tables/table_card.rb +61 -0
- data/app/views/rails_devtools/gems/gem_card.rb +74 -0
- data/app/views/rails_devtools/gems/index.rb +32 -0
- data/app/views/rails_devtools/image_assets/image_card.rb +37 -0
- data/app/views/rails_devtools/image_assets/image_details.rb +82 -0
- data/app/views/rails_devtools/image_assets/index.rb +39 -0
- data/app/views/rails_devtools/routes/index.rb +37 -0
- data/app/views/rails_devtools/routes/route_card.rb +70 -0
- data/app/views/rails_devtools/routes/route_details/controller_card.rb +87 -0
- data/app/views/rails_devtools/routes/route_details/route_path_input.rb +46 -0
- data/app/views/rails_devtools/routes/route_details.rb +171 -0
- data/config/routes.rb +24 -0
- data/lib/rails_devtools/asset_config.rb +52 -0
- data/lib/rails_devtools/asset_providers/jsbundling_rails_config.rb +21 -0
- data/lib/rails_devtools/asset_providers/propshaft_config.rb +19 -0
- data/lib/rails_devtools/asset_providers/shakapacker_config.rb +26 -0
- data/lib/rails_devtools/asset_providers/sprocket_config.rb +19 -0
- data/lib/rails_devtools/asset_providers/vite_rails_config.rb +19 -0
- data/lib/rails_devtools/engine.rb +22 -0
- data/lib/rails_devtools/importmap.rb +16 -0
- data/lib/rails_devtools/importmaps/base.rb +82 -0
- data/lib/rails_devtools/version.rb +3 -0
- data/lib/rails_devtools.rb +20 -0
- data/lib/tasks/rails_devtools_tasks.rake +4 -0
- data/vendor/javascript/@stimulus-components--clipboard.js +4 -0
- data/vendor/javascript/@stimulus-components--notification.js +4 -0
- data/vendor/javascript/@stimulus-components--reveal.js +4 -0
- data/vendor/javascript/stimulus-autoloader.js +54 -0
- data/vendor/javascript/stimulus-importmap-autoloader.js +27 -0
- data/vendor/javascript/stimulus-loading.js +93 -0
- data/vendor/javascript/stimulus-use.js +4 -0
- data/vendor/javascript/stimulus.min.js +5 -0
- data/vendor/javascript/turbo.min.js +36 -0
- metadata +241 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class Lucide::PocketKnife < Lucide::Base
|
6
|
+
def view_template
|
7
|
+
svg(
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
9
|
+
width: width,
|
10
|
+
height: height,
|
11
|
+
viewbox: "0 0 24 24",
|
12
|
+
fill: "none",
|
13
|
+
stroke: "currentColor",
|
14
|
+
stroke_width: "2",
|
15
|
+
stroke_linecap: "round",
|
16
|
+
stroke_linejoin: "round",
|
17
|
+
class: "lucide lucide-pocket-knife"
|
18
|
+
) do |s|
|
19
|
+
s.path(d: "M3 2v1c0 1 2 1 2 2S3 6 3 7s2 1 2 2-2 1-2 2 2 1 2 2")
|
20
|
+
s.path(d: "M18 6h.01")
|
21
|
+
s.path(d: "M6 18h.01")
|
22
|
+
s.path(d: "M20.83 8.83a4 4 0 0 0-5.66-5.66l-12 12a4 4 0 1 0 5.66 5.66Z")
|
23
|
+
s.path(d: "M18 11.66V22a4 4 0 0 0 4-4V6")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class Lucide::SignPost < Lucide::Base
|
6
|
+
def view_template
|
7
|
+
svg(
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
9
|
+
width: width,
|
10
|
+
height: height,
|
11
|
+
viewbox: "0 0 24 24",
|
12
|
+
fill: "none",
|
13
|
+
stroke: "currentColor",
|
14
|
+
stroke_width: "2",
|
15
|
+
stroke_linecap: "round",
|
16
|
+
stroke_linejoin: "round",
|
17
|
+
class: "lucide lucide-signpost"
|
18
|
+
) do |s|
|
19
|
+
s.path(d: "M12 13v8")
|
20
|
+
s.path(d: "M12 3v3")
|
21
|
+
s.path(
|
22
|
+
d:
|
23
|
+
"M18 6a2 2 0 0 1 1.387.56l2.307 2.22a1 1 0 0 1 0 1.44l-2.307 2.22A2 2 0 0 1 18 13H6a2 2 0 0 1-1.387-.56l-2.306-2.22a1 1 0 0 1 0-1.44l2.306-2.22A2 2 0 0 1 6 6z"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class Lucide::Trash < Lucide::Base
|
6
|
+
def view_template
|
7
|
+
svg(
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
9
|
+
width: width,
|
10
|
+
height: height,
|
11
|
+
viewbox: "0 0 24 24",
|
12
|
+
fill: "none",
|
13
|
+
stroke: "currentColor",
|
14
|
+
stroke_width: "2",
|
15
|
+
stroke_linecap: "round",
|
16
|
+
stroke_linejoin: "round",
|
17
|
+
class: "lucide lucide-trash"
|
18
|
+
) do |s|
|
19
|
+
s.path(d: "M3 6h18")
|
20
|
+
s.path(d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6")
|
21
|
+
s.path(d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class Lucide::TriangleAlert < Lucide::Base
|
6
|
+
def view_template
|
7
|
+
svg(
|
8
|
+
xmlns: "http://www.w3.org/2000/svg",
|
9
|
+
width: width,
|
10
|
+
height: height,
|
11
|
+
viewbox: "0 0 24 24",
|
12
|
+
fill: "none",
|
13
|
+
stroke: "currentColor",
|
14
|
+
stroke_width: "2",
|
15
|
+
stroke_linecap: "round",
|
16
|
+
stroke_linejoin: "round",
|
17
|
+
class: "lucide lucide-triangle-alert"
|
18
|
+
) do |s|
|
19
|
+
s.path(
|
20
|
+
d:
|
21
|
+
"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"
|
22
|
+
)
|
23
|
+
s.path(d: "M12 9v4")
|
24
|
+
s.path(d: "M12 17h.01")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class PageContent < Components::ApplicationComponent
|
6
|
+
def view_template(&)
|
7
|
+
turbo_frame_tag("page_content", &)
|
8
|
+
end
|
9
|
+
|
10
|
+
def page_title(&)
|
11
|
+
h1(class: "text-2xl font-bold", &)
|
12
|
+
end
|
13
|
+
|
14
|
+
def search_form(form:, path:, method: :get)
|
15
|
+
render Components::Ui::SearchForm.new(
|
16
|
+
form: form,
|
17
|
+
path: path,
|
18
|
+
method: method
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def results(&)
|
23
|
+
div(class: "mt-4", &)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class Ui::Drawer < Components::ApplicationComponent
|
6
|
+
def initialize(id:, direction: "left", classes: "")
|
7
|
+
@id = id
|
8
|
+
@direction = direction
|
9
|
+
@classes = classes
|
10
|
+
end
|
11
|
+
|
12
|
+
def view_template(&block)
|
13
|
+
div(class: drawer_classes, data_controller: "checkbox") do
|
14
|
+
input(id: @id, type: "checkbox", class: "drawer-toggle", data_checkbox_target: "checkbox")
|
15
|
+
block.call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def content(&)
|
20
|
+
div(class: "drawer-content flex flex-col", &)
|
21
|
+
end
|
22
|
+
|
23
|
+
def drawer_side(&block)
|
24
|
+
div(class: "drawer-side") do
|
25
|
+
label(
|
26
|
+
for: @id,
|
27
|
+
aria_label: "close sidebar",
|
28
|
+
class: "drawer-overlay"
|
29
|
+
)
|
30
|
+
block.call
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def drawer_classes
|
37
|
+
[
|
38
|
+
"drawer",
|
39
|
+
direction_class,
|
40
|
+
@classes
|
41
|
+
].join(" ")
|
42
|
+
end
|
43
|
+
|
44
|
+
def direction_class
|
45
|
+
@direction == "left" ? "" : "drawer-end"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class Components::Ui::Menu < Components::ApplicationComponent
|
5
|
+
include Phlex::Rails::Helpers::CurrentPage
|
6
|
+
|
7
|
+
def view_template
|
8
|
+
large_screen_menu
|
9
|
+
small_screen_menu
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def large_screen_menu
|
15
|
+
div(class: "hidden lg:block h-full") do
|
16
|
+
menu_items(classes: "w-60 p-4") do
|
17
|
+
div(class: "border-b-2 border-base-300 pl-4 pb-4 mb-2") do
|
18
|
+
menu_title
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def small_screen_menu
|
25
|
+
div(
|
26
|
+
data_controller: "reveal",
|
27
|
+
data_reveal_hidden_class: "hidden",
|
28
|
+
class: "lg:hidden w-full p-3 bg-base-200"
|
29
|
+
) do
|
30
|
+
div(class: "flex flex-col") do
|
31
|
+
div(class: "flex justify-between") do
|
32
|
+
menu_title
|
33
|
+
button(data_action: " click->reveal#toggle", type: "button") do
|
34
|
+
render Components::Lucide::Menu
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
div(
|
39
|
+
data_reveal_target: "item",
|
40
|
+
class: "hidden mt-2 pl-3 border-t-2 border-base-300 "
|
41
|
+
) do
|
42
|
+
menu_items(classes: "")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def menu_title
|
48
|
+
div(class: "flex gap-3") do
|
49
|
+
render Components::Lucide::PocketKnife
|
50
|
+
h2(class: "font-bold text-xl") { "Devtools" }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
MenuItem = Data.define(:icon, :name, :path)
|
55
|
+
|
56
|
+
def menu_items(classes:, &block)
|
57
|
+
ul(class: [classes, "menu bg-base-200 text-base-content h-full"]) do
|
58
|
+
block.call if block_given?
|
59
|
+
|
60
|
+
items.each do |menu_item|
|
61
|
+
item = MenuItem.new(**menu_item)
|
62
|
+
li do
|
63
|
+
a(href: item.path, class: current_page?(item.path) && "active") do
|
64
|
+
span(class: "mr-1") { render item.icon.new(width: 16, height: 16) }
|
65
|
+
span { item.name }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def items
|
73
|
+
[
|
74
|
+
{ icon: Lucide::Database, name: "Database tables", path: helpers.database_tables_path },
|
75
|
+
{ icon: Lucide::Package, name: "Gems", path: helpers.gems_path },
|
76
|
+
{ icon: Lucide::SignPost, name: "Routes", path: helpers.routes_path },
|
77
|
+
{ icon: Lucide::Images, name: "Image assets", path: helpers.image_assets_path }
|
78
|
+
]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
module Components
|
5
|
+
class Ui::SearchForm < Components::ApplicationComponent
|
6
|
+
include Phlex::Rails::Helpers::FormWith
|
7
|
+
include Phlex::Rails::Helpers::Request
|
8
|
+
|
9
|
+
def initialize(form:, path:, method:)
|
10
|
+
@form = form
|
11
|
+
@path = path
|
12
|
+
@method = method
|
13
|
+
end
|
14
|
+
|
15
|
+
def view_template
|
16
|
+
div(class: "flex gap-2 mt-4") do
|
17
|
+
form_with(
|
18
|
+
model: @form,
|
19
|
+
url: @path,
|
20
|
+
method: @method,
|
21
|
+
data: { turbo_action: :advance },
|
22
|
+
class: "w-full max-w-sm"
|
23
|
+
) do |form|
|
24
|
+
label(class: "input input-bordered flex items-center gap-2 w-full grow") do
|
25
|
+
form.text_field(:search,
|
26
|
+
{ class: " w-full ", placeholder: "Type search then enter", value: search_params })
|
27
|
+
search_icon
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
reset_button if search_params.present?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def reset_button
|
38
|
+
link_to(@path, class: "btn text-neutral", data: { turbo_action: :advance }) do
|
39
|
+
render Components::Lucide::Close.new(width: 16, height: 16)
|
40
|
+
plain "Reset"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def search_params
|
45
|
+
request.params.dig(@form.model_name.param_key.to_sym, :search)
|
46
|
+
end
|
47
|
+
|
48
|
+
def search_icon
|
49
|
+
svg(
|
50
|
+
xmlns: "http://www.w3.org/2000/svg",
|
51
|
+
viewbox: "0 0 16 16",
|
52
|
+
fill: "currentColor",
|
53
|
+
class: "h-4 w-4 opacity-70"
|
54
|
+
) do |s|
|
55
|
+
s.path(
|
56
|
+
fill_rule: "evenodd",
|
57
|
+
d:
|
58
|
+
"M9.965 11.026a5 5 0 1 1 1.06-1.06l2.755 2.754a.75.75 0 1 1-1.06 1.06l-2.755-2.754ZM10.5 7a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Z",
|
59
|
+
clip_rule: "evenodd"
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class DatabaseTables::Index < ApplicationView
|
5
|
+
def initialize(form: nil, tables: [])
|
6
|
+
@tables = tables
|
7
|
+
@form = form
|
8
|
+
end
|
9
|
+
|
10
|
+
def view_template
|
11
|
+
render Components::PageContent.new do |page|
|
12
|
+
page.page_title { "Database tables" }
|
13
|
+
page.search_form(form: @form, path: helpers.database_tables_path)
|
14
|
+
page.results { results }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def results
|
21
|
+
if @tables.empty?
|
22
|
+
div(class: "text-neutral") { "No results found" }
|
23
|
+
else
|
24
|
+
div(class: "grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 3xl:grid-cols-4 gap-2 w-full items-start") do
|
25
|
+
@tables.each do |table|
|
26
|
+
render DatabaseTables::TableCard.new(table: table)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class DatabaseTables::TableCard < Components::ApplicationComponent
|
5
|
+
def initialize(table:)
|
6
|
+
@table = table
|
7
|
+
end
|
8
|
+
|
9
|
+
def view_template
|
10
|
+
div(class: "card bg-white text-sm w-full shadow-sm") do
|
11
|
+
div(class: "card-body") do
|
12
|
+
h2(class: "card-title") { @table.table_name.capitalize }
|
13
|
+
columns_list
|
14
|
+
indexes_list
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def columns_list
|
22
|
+
div(class: "flex flex-col gap-2 divide-y divide-base-200") do
|
23
|
+
@table.columns.each do |column|
|
24
|
+
div(class: "flex gap-x-2 text-neutral pt-2 justify-between items-center") do
|
25
|
+
div(class: "flex gap-x-2") do
|
26
|
+
div(class: "font-bold") { column.name }
|
27
|
+
div(class: "text-neutral italic") { column.type }
|
28
|
+
end
|
29
|
+
span(class: "text-xs opacity-75") { column.default } if column.default
|
30
|
+
div(class: "text-xs opacity-75") { "not null" } unless column.null
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def indexes_list
|
37
|
+
div(class: "mt-8") do
|
38
|
+
h3(class: "text-lg font-bold") { "#{@table.table_name.capitalize} indexes" }
|
39
|
+
div(class: "mt-2 flex flex-col gap-2 divide-y divide-base-200") do
|
40
|
+
@table.indexes.each do |index|
|
41
|
+
div(class: "flex gap-x-2 text-neutral pt-2 justify-between items-center") do
|
42
|
+
div do
|
43
|
+
div(class: "font-bold") { index.name }
|
44
|
+
div(class: "text-xs opacity-75") do
|
45
|
+
[index.unique ? "unique" : nil, index_columns_text(index.columns)].compact.join(" ")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def index_columns_text(columns)
|
55
|
+
columns_list = columns.join(", ")
|
56
|
+
return columns_list if columns.size == 1
|
57
|
+
|
58
|
+
"composite of #{columns_list}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class Gems::GemCard < Components::ApplicationComponent
|
5
|
+
include Phlex::Rails::Helpers::DistanceOfTimeInWords
|
6
|
+
|
7
|
+
def initialize(gem:)
|
8
|
+
@gem = gem
|
9
|
+
end
|
10
|
+
|
11
|
+
def view_template
|
12
|
+
div(class: "card card-compact bg-white text-sm w-full shadow-sm") do
|
13
|
+
div(class: "card-body") do
|
14
|
+
div(class: "card-actions") do
|
15
|
+
@gem.groups.each do |group|
|
16
|
+
div(class: "badge badge-sm") { group }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
h2(class: "inline-flex card-title justify-between items-center") do
|
20
|
+
div(class: "flex items-center gap-2") do
|
21
|
+
span do
|
22
|
+
[@gem.name.titleize.capitalize, @gem.actual_version].join(" ")
|
23
|
+
end
|
24
|
+
span(class: "badge badge-sm badge-warning font-normal") { "outdated" } if @gem.outdated?
|
25
|
+
end
|
26
|
+
span(class: "text-sm text-neutral opacity-75 font-normal") do
|
27
|
+
@gem.date.strftime("%b %d, %Y")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
div(class: "text-neutral opacity-75") do
|
32
|
+
outdated_info
|
33
|
+
div { @gem.summary }
|
34
|
+
end
|
35
|
+
div(class: "card-actions mt-4 justify-end") do
|
36
|
+
if @gem.source_code
|
37
|
+
a(href: @gem.source_code, class: "btn btn-xs btn-outline btn-secondary") do
|
38
|
+
"Source Code"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
if @gem.documentation
|
42
|
+
a(href: @gem.documentation, class: "btn btn-xs btn-outline btn-secondary") do
|
43
|
+
"Documentation"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
a(href: @gem.homepage, class: "btn btn-xs") { "Homepage" } if @gem.homepage
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def outdated_info
|
55
|
+
return unless @gem.outdated?
|
56
|
+
|
57
|
+
div(class: "alert mb-4 flex justify-between") do
|
58
|
+
div do
|
59
|
+
h4(class: "font-bold text-left") { "Version #{@gem.latest_version.version} available" }
|
60
|
+
p(class: "text-left") do
|
61
|
+
"Your version came out #{months_since_last_update} months before the current release"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
div do
|
65
|
+
a(href: @gem.changelog, class: "btn btn-sm btn-secondary") { "Changelog" } if @gem.changelog
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def months_since_last_update
|
71
|
+
(@gem.latest_version.date - @gem.date).seconds.in_months.to_i
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class Gems::Index < ApplicationView
|
5
|
+
def initialize(form: nil, gems: [])
|
6
|
+
@gems = gems
|
7
|
+
@form = form
|
8
|
+
end
|
9
|
+
|
10
|
+
def view_template
|
11
|
+
render Components::PageContent.new do |page|
|
12
|
+
page.page_title { "Gems" }
|
13
|
+
page.search_form(form: @form, path: helpers.gems_path)
|
14
|
+
page.results { results }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def results
|
21
|
+
if @gems.empty?
|
22
|
+
div(class: "text-neutral") { "No results found" }
|
23
|
+
else
|
24
|
+
div(class: "w-full flex flex-col gap-y-2") do
|
25
|
+
@gems.each do |gem|
|
26
|
+
render Gems::GemCard.new(gem: gem)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class ImageAssets::ImageCard < Components::ApplicationComponent
|
5
|
+
def initialize(image_info:)
|
6
|
+
@image_info = image_info
|
7
|
+
end
|
8
|
+
|
9
|
+
def view_template
|
10
|
+
turbo_frame_tag(@image_info.full_path) do
|
11
|
+
a(
|
12
|
+
href: helpers.image_asset_path(
|
13
|
+
@image_info.name,
|
14
|
+
full_name: @image_info.basename,
|
15
|
+
image_path: @image_info.full_path
|
16
|
+
),
|
17
|
+
data: { turbo_frame: 'drawer_content', action: 'click->checkbox#toggle' },
|
18
|
+
class: 'group'
|
19
|
+
) do
|
20
|
+
div(class: 'card card-compact bg-white shadow-sm group-hover:bg-primary w-[150px]') do
|
21
|
+
figure do
|
22
|
+
img(
|
23
|
+
src: helpers.host_app_image_path(@image_info.devtools_image_path),
|
24
|
+
class: 'card-image',
|
25
|
+
width: '150'
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
div(class: 'card-body group-hover:text-primary-content') do
|
30
|
+
p(class: 'text-xs truncate') { @image_info.basename }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsDevtools
|
4
|
+
class ImageAssets::ImageDetails < Components::ApplicationComponent
|
5
|
+
def initialize(image_info:)
|
6
|
+
@image_info = image_info
|
7
|
+
end
|
8
|
+
|
9
|
+
def view_template
|
10
|
+
turbo_frame_tag("drawer_content", class: "flex flex-col") do
|
11
|
+
figure do
|
12
|
+
img(
|
13
|
+
src: helpers.host_app_image_path(@image_info.devtools_image_path),
|
14
|
+
width: "400"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
div(class: "mt-4") do
|
19
|
+
h3(class: "text-lg font-bold") { @image_info.basename }
|
20
|
+
div(class: "mt-4 pt-4 border-t-2 border-base-300 grid grid-cols-3 gap-x-4 gap-y-2 mt-2 text-sm text-neutral") do
|
21
|
+
# Image size
|
22
|
+
div(class: "text-right font-bold ") { "Image size" }
|
23
|
+
div(class: "col-span-2") { "#{@image_info.width} x #{@image_info.height}" }
|
24
|
+
|
25
|
+
# File size
|
26
|
+
div(class: "text-right font-bold text-neutral") { "File size" }
|
27
|
+
div(class: "col-span-2") { bytes_to_kb(@image_info.file_size) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
image_tag_input
|
32
|
+
|
33
|
+
div(class: "mt-8 pt-8 border-t-2 border-base-300 flex gap-x-2 justify-end") do
|
34
|
+
delete_button
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def image_tag_input
|
40
|
+
div(class: "mt-8 w-full") do
|
41
|
+
div(
|
42
|
+
class: "join w-full",
|
43
|
+
data_controller: "clipboard",
|
44
|
+
data_clipboard_success_content_value: "Copied!"
|
45
|
+
) do
|
46
|
+
input(
|
47
|
+
value: @image_info.image_helper_snippet,
|
48
|
+
class: "input input-bordered input-primary input-sm w-full join-item",
|
49
|
+
data_clipboard_target: "source"
|
50
|
+
)
|
51
|
+
button(
|
52
|
+
class: "btn btn-primary btn-outline btn-sm join-item",
|
53
|
+
data_action: "clipboard#copy",
|
54
|
+
data_clipboard_target: "button"
|
55
|
+
) { "Copy" }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete_button
|
61
|
+
button_to(
|
62
|
+
helpers.image_asset_path(@image_info.name, image_path: @image_info.full_path),
|
63
|
+
class: "btn btn-outline btn-error btn-sm",
|
64
|
+
method: :delete,
|
65
|
+
form: { data: {
|
66
|
+
turbo_confirm: "Are you sure you want to delete this image?",
|
67
|
+
action: "turbo:submit-end->checkbox#toggle"
|
68
|
+
} }
|
69
|
+
) do
|
70
|
+
span { render Components::Lucide::Trash.new(width: 16, height: 16) }
|
71
|
+
plain "delete"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def bytes_to_kb(bytes)
|
78
|
+
kb = bytes.to_f / 1024
|
79
|
+
"#{kb.round(2)} KB"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|