kommandant 0.1.0 → 0.2.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/app/assets/builds/kommandant.css +634 -0
- data/app/assets/stylesheets/kommandant/application.tailwind.css +3 -0
- data/app/controllers/concerns/kommandant/recent_commands.rb +21 -0
- data/app/controllers/kommandant/application_controller.rb +6 -1
- data/app/controllers/kommandant/commands/searches_controller.rb +40 -0
- data/app/controllers/kommandant/commands_controller.rb +17 -0
- data/app/controllers/kommandant/searches_controller.rb +14 -0
- data/app/models/kommandant/command.rb +88 -0
- data/app/models/kommandant/commands/search_result.rb +44 -0
- data/app/views/kommandant/commands/searches/show.html.erb +69 -0
- data/app/views/kommandant/commands/show.html.erb +20 -0
- data/app/views/kommandant/searches/index.html.erb +1 -0
- data/app/views/kommandant/searches/new.html.erb +1 -0
- data/app/views/kommandant/shared/_command_palette.html.erb +53 -0
- data/app/views/kommandant/shared/command_palette/_command.html.erb +14 -0
- data/app/views/kommandant/shared/command_palette/_default_state.html.erb +14 -0
- data/app/views/kommandant/shared/command_palette/_empty_state.html.erb +7 -0
- data/app/views/kommandant/shared/command_palette/_loading_message.html.erb +7 -0
- data/app/views/kommandant/shared/command_palette/_result.html.erb +6 -0
- data/app/views/kommandant/shared/icons/_chevron_right.html.erb +3 -0
- data/app/views/kommandant/shared/icons/_command.erb +7 -0
- data/app/views/kommandant/shared/icons/_search.erb +3 -0
- data/app/views/kommandant/shared/icons/_spinner.erb +4 -0
- data/config/locales/da.yml +19 -0
- data/config/locales/en.yml +19 -0
- data/config/routes.rb +4 -0
- data/lib/kommandant/engine.rb +7 -0
- data/lib/kommandant/version.rb +1 -1
- data/lib/kommandant.rb +21 -1
- metadata +89 -3
@@ -0,0 +1,17 @@
|
|
1
|
+
module Kommandant
|
2
|
+
class CommandsController < ApplicationController
|
3
|
+
before_action :set_command
|
4
|
+
|
5
|
+
def show
|
6
|
+
if Kommandant.config.recent_commands.enabled
|
7
|
+
current_user.recent_commands.prepend(@command.id)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def set_command
|
14
|
+
@command = Kommandant::Command.find(params[:id])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Kommandant
|
2
|
+
class SearchesController < ApplicationController
|
3
|
+
def index
|
4
|
+
@results = Kommandant::Command.search(params[:query])
|
5
|
+
|
6
|
+
unless Kommandant.config.admin_only_filter_lambda.present? && Kommandant.config.admin_only_filter_lambda.call(current_user, current_admin)
|
7
|
+
@results.reject!(&:admin_only?)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Kommandant
|
2
|
+
class Command
|
3
|
+
class << self
|
4
|
+
def reindex!
|
5
|
+
index.add_documents(
|
6
|
+
JSON.parse(File.read(Kommandant.config.commands_path))
|
7
|
+
)
|
8
|
+
end
|
9
|
+
|
10
|
+
def search(query)
|
11
|
+
result = index.search(query)["hits"]
|
12
|
+
|
13
|
+
result.map do |command|
|
14
|
+
new(**command.symbolize_keys)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def find(id)
|
19
|
+
search(id).find { |command| command.id == id }
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
JSON.parse(File.read(Kommandant.config.commands_path)).map do |hash|
|
24
|
+
Kommandant::Command.new(**hash.symbolize_keys)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def index
|
29
|
+
client.index("commands")
|
30
|
+
end
|
31
|
+
|
32
|
+
def client
|
33
|
+
MeiliSearch::Rails.client
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(id:, icon:, path:, http_method:, resource_class: nil, redirect_path: nil, text_keys: [], admin_only: false, translations: {})
|
38
|
+
@id = id
|
39
|
+
@icon = icon
|
40
|
+
@path = path
|
41
|
+
@http_method = http_method
|
42
|
+
@resource_class = resource_class
|
43
|
+
@redirect_path = redirect_path
|
44
|
+
@text_keys = text_keys
|
45
|
+
@admin_only = admin_only
|
46
|
+
@translator = I18n::Backend::KeyValue.new({})
|
47
|
+
translations.each_pair do |locale, data|
|
48
|
+
@translator.store_translations(locale, data)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(other)
|
53
|
+
instance_variables.all? do |ivar|
|
54
|
+
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
attr_reader :id, :path, :http_method, :resource_class, :redirect_path, :text_keys, :translator
|
59
|
+
|
60
|
+
def name
|
61
|
+
return "no translation for #{I18n.locale}.name in Meilisearch" unless translator.exists?(I18n.locale, "name")
|
62
|
+
|
63
|
+
translator.translate(I18n.locale, "name")
|
64
|
+
end
|
65
|
+
|
66
|
+
def placeholder
|
67
|
+
return unless translator.exists?(I18n.locale, "placeholder")
|
68
|
+
|
69
|
+
translator.translate(I18n.locale, "placeholder")
|
70
|
+
end
|
71
|
+
|
72
|
+
def admin_only?
|
73
|
+
admin_only
|
74
|
+
end
|
75
|
+
|
76
|
+
def icon?
|
77
|
+
icon.present?
|
78
|
+
end
|
79
|
+
|
80
|
+
def icon
|
81
|
+
@icon.to_s.dasherize
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
attr_reader :admin_only
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Kommandant
|
2
|
+
class Commands::SearchResult
|
3
|
+
def initialize(command:, resource:)
|
4
|
+
@command = command
|
5
|
+
@resource = resource
|
6
|
+
@translator = command.translator
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :resource
|
10
|
+
delegate :icon, to: :command
|
11
|
+
|
12
|
+
def name
|
13
|
+
if translator.exists?(I18n.locale, "result_text")
|
14
|
+
translation_attributes = {}
|
15
|
+
command.text_keys.each do |key|
|
16
|
+
translation_attributes[key.to_sym] = indexed_attribute_for(resource, key)
|
17
|
+
end
|
18
|
+
translator.translate(I18n.locale, "result_text", **translation_attributes)
|
19
|
+
else
|
20
|
+
command.text_keys.map do |text_key|
|
21
|
+
indexed_attribute_for(resource, text_key)
|
22
|
+
end.join(", ")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def path
|
27
|
+
return command.redirect_path.gsub(":id", resource.id.to_s) if command.redirect_path
|
28
|
+
|
29
|
+
"/#{command.resource_class.downcase.pluralize}/#{resource.id}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def http_method
|
33
|
+
:get
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :command, :translator
|
39
|
+
|
40
|
+
def indexed_attribute_for(resource, text_key)
|
41
|
+
resource.meilisearch_settings.get_attributes(resource)[text_key]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
<%= turbo_frame_tag :command_palette do %>
|
2
|
+
<div data-command-palette-target="content">
|
3
|
+
<div class="kommandant-flex kommandant-px-4">
|
4
|
+
<div class="kommandant-flex kommandant-items-center kommandant-space-x-1 kommandant-text-gray-400">
|
5
|
+
<%= link_to(Kommandant::Engine.routes.url_helpers.searches_path, class: "kommandant-inline-flex kommandant-items-center kommandant-space-x-2 kommandant-border kommandant-shadow-sm kommandant-font-medium kommandant-rounded-md focus:kommandant-outline-none focus:kommandant-ring-2 focus:kommandant-ring-offset-2 disabled:kommandant-opacity-50 disabled:kommandant-cursor-not-allowed kommandant-border-gray-300 kommandant-text-gray-700 kommandant-bg-white hover:kommandant-bg-gray-50 hover:kommandant-cursor-pointer focus:kommandant-ring-gray-500 kommandant-text-xs kommandant-px-2.5 kommandant-py-1.5") do %>
|
6
|
+
<span><%= @command.name %></span>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<%= render "kommandant/shared/icons/chevron_right" %>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<%= form_with url: Kommandant::Engine.routes.url_helpers.command_searches_path(@command.id), method: :get, class: "kommandant-flex-grow" do |form| %>
|
13
|
+
<%= form.text_field :query, class: "kommandant-h-12 kommandant-w-full kommandant-border-0 kommandant-bg-transparent kommandant-pl-1 kommandant-pr-4 kommandant-text-gray-800 kommandant-placeholder-gray-400 focus:kommandant-ring-0 sm:kommandant-text-sm", value: params[:query], placeholder: @command.placeholder, autocomplete: "off", data: {command_palette_target: "input", action: "keyup->command-palette#submit"} %>
|
14
|
+
<% end %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<% if @results.any? %>
|
18
|
+
<ul class="kommandant-overflow-y-auto kommandant-p-2 kommandant-text-sm kommandant-text-gray-700">
|
19
|
+
<% @results.each do |result| %>
|
20
|
+
<%# TODO: How about a default? %>
|
21
|
+
<% if partial_exists_for_result?(result) %>
|
22
|
+
<%= render result.resource, result: result %>
|
23
|
+
<% else %>
|
24
|
+
<%= render "kommandant/shared/command_palette/result", result: result %>
|
25
|
+
<% end %>
|
26
|
+
<% end %>
|
27
|
+
</ul>
|
28
|
+
|
29
|
+
<% if Kommandant.config.pagination.enabled %>
|
30
|
+
<div class="kommandant-px-4 kommandant-py-2 kommandant-flex kommandant-justify-between kommandant-items-baseline">
|
31
|
+
<span class="text-sm"><%= @pagination_info_label %></span>
|
32
|
+
|
33
|
+
<div class="kommandant-space-x-4">
|
34
|
+
<%= link_to(t("kommandant.pagination.prev"), Kommandant::Engine.routes.url_helpers.command_searches_path(@command.id, page: @pagination.prev, query: @query), class: "kommandant-font-medium kommandant-text-sm kommandant-text-gray-600 hover:kommandant-text-gray-800", data: {command_palette_target: "previousPageLink"}) if @pagination.prev %>
|
35
|
+
<%= link_to(t("kommandant.pagination.next"), Kommandant::Engine.routes.url_helpers.command_searches_path(@command.id, page: @pagination.next, query: @query), class: "kommandant-font-medium kommandant-text-sm kommandant-text-gray-600 hover:kommandant-text-gray-800", data: {command_palette_target: "nextPageLink"}) if @pagination.next %>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
<% end %>
|
39
|
+
<% else %>
|
40
|
+
<%= render "kommandant/shared/command_palette/empty_state", query: params[:query] %>
|
41
|
+
<% end %>
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<%= render "kommandant/shared/command_palette/loading_message" %>
|
45
|
+
|
46
|
+
<% if Kommandant.config.pagination.enabled && @pagination.pages > 1 %>
|
47
|
+
<div class="kommandant-flex kommandant-justify-end kommandant-items-center kommandant-divide-x kommandant-bg-gray-50 kommandant-py-2.5 kommandant-px-4 kommandant-text-xs kommandant-text-gray-700">
|
48
|
+
<div class="kommandant-pr-2 kommandant-flex kommandant-flex-wrap kommandant-justify-end kommandant-items-center kommandant-space-x-2">
|
49
|
+
<span><%= t("kommandant.pagination.prev").html_safe %>:</span>
|
50
|
+
|
51
|
+
<div class="kommandant-flex kommandant-items-center kommandant-space-x-1">
|
52
|
+
<kbd class="kommandant-px-1 kommandant-py-0.5 kommandant-rounded kommandant-border kommandant-border-gray-400 kommandant-bg-white kommandant-font-semibold kommandant-text-gray-900">Ctrl</kbd>
|
53
|
+
<span>+</span>
|
54
|
+
<kbd class="kommandant-px-1 kommandant-py-0.5 kommandant-rounded kommandant-border kommandant-border-gray-400 kommandant-bg-white kommandant-font-semibold kommandant-text-gray-900">J</kbd>
|
55
|
+
</div>
|
56
|
+
</div>
|
57
|
+
|
58
|
+
<div class="kommandant-pl-2 kommandant-flex kommandant-flex-wrap kommandant-justify-end kommandant-items-center kommandant-space-x-2">
|
59
|
+
<span><%= t("kommandant.pagination.next").html_safe %>:</span>
|
60
|
+
|
61
|
+
<div class="kommandant-flex kommandant-items-center kommandant-space-x-1">
|
62
|
+
<kbd class="kommandant-px-1 kommandant-py-0.5 kommandant-rounded kommandant-border kommandant-border-gray-400 kommandant-bg-white kommandant-font-semibold kommandant-text-gray-900">Ctrl</kbd>
|
63
|
+
<span>+</span>
|
64
|
+
<kbd class="kommandant-px-1 kommandant-py-0.5 kommandant-rounded kommandant-border kommandant-border-gray-400 kommandant-bg-white kommandant-font-semibold kommandant-text-gray-900">L</kbd>
|
65
|
+
</div>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
<% end %>
|
69
|
+
<% end %>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<%= turbo_frame_tag :command_palette do %>
|
2
|
+
<div data-command-palette-target="content">
|
3
|
+
<div class="kommandant-flex kommandant-px-4">
|
4
|
+
<div class="kommandant-flex kommandant-items-center kommandant-space-x-1 kommandant-text-gray-400">
|
5
|
+
<%= link_to(Kommandant::Engine.routes.url_helpers.searches_path, class: "kommandant-inline-flex kommandant-items-center kommandant-space-x-2 kommandant-border kommandant-shadow-sm kommandant-font-medium kommandant-rounded-md focus:kommandant-outline-none focus:kommandant-ring-2 focus:kommandant-ring-offset-2 disabled:kommandant-opacity-50 disabled:kommandant-cursor-not-allowed kommandant-border-gray-300 kommandant-text-gray-700 kommandant-bg-white hover:kommandant-bg-gray-50 hover:kommandant-cursor-pointer focus:kommandan
|
6
|
+
tring-gray-500 kommandant-text-xs kommandant-px-2.5 kommandant-py-1.5") do %>
|
7
|
+
<span><%= @command.name %></span>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<%= render "kommandant/shared/icons/chevron_right" %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<%= form_with url: Kommandant::Engine.routes.url_helpers.command_searches_path(@command.id), method: :get, class: "kommandant-flex-grow" do |form| %>
|
14
|
+
<%= form.text_field :query, class: "kommandant-h-12 kommandant-w-full kommandant-border-0 kommandant-bg-transparent kommandant-pl-1 kommandant-pr-4 kommandant-text-gray-800 kommandant-placeholder-gray-400 focus:kommandant-ring-0 sm:kommandant-text-sm", value: @query, placeholder: @command.placeholder, autocomplete: "off", data: {command_palette_target: "input", action: "keyup->command-palette#submit"} %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<%= render "kommandant/shared/command_palette/loading_message" %>
|
20
|
+
<% end %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render "kommandant/shared/command_palette", results: @results, query: params[:query] %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render "kommandant/shared/command_palette", recent_commands: @recent_commands %>
|
@@ -0,0 +1,53 @@
|
|
1
|
+
<div class="kommandant-relative kommandant-z-50" role="dialog" aria-modal="true" data-action="click->command-palette#hideWithBackgroundOverlay keydown.ctrl+l->command-palette#nextPage keydown.ctrl+j->command-palette#previousPage keydown.meta+l->command-palette#nextPage keydown.meta+j->command-palette#previousPage">
|
2
|
+
<div class="hidden kommandant-fixed kommandant-inset-0 kommandant-bg-gray-500 kommandant-bg-opacity-25 kommandant-transition-opacity"
|
3
|
+
data-command-palette-target="background"
|
4
|
+
data-transition-enter="kommandant-ease-out kommandant-duration-300"
|
5
|
+
data-transition-enter-start="kommandant-opacity-0"
|
6
|
+
data-transition-enter-end="kommandant-opacity-100"
|
7
|
+
data-transition-leave="kommandant-ease-in kommandant-duration-200"
|
8
|
+
data-transition-leave-start="kommandant-opacity-100"
|
9
|
+
data-transition-leave-end="kommandant-opacity-0">
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="hidden kommandant-fixed kommandant-inset-0 kommandant-z-10 kommandant-overflow-y-auto kommandant-p-4 sm:kommandant-p-6 md:kommandant-p-20"
|
13
|
+
data-command-palette-target="panel"
|
14
|
+
data-transition-enter="kommandant-ease-out kommandant-duration-300"
|
15
|
+
data-transition-enter-start="kommandant-opacity-0 kommandant-scale-95"
|
16
|
+
data-transition-enter-end="kommandant-opacity-100 kommandant-scale-100"
|
17
|
+
data-transition-leave="kommandant-ease-in kommandant-duration-200"
|
18
|
+
data-transition-leave-start="kommandant-opacity-100 kommandant-scale-100"
|
19
|
+
data-transition-leave-end="kommandant-opacity-0 kommandant-scale-95">
|
20
|
+
|
21
|
+
<div class="kommandant-mx-auto kommandant-max-w-2xl kommandant-transform kommandant-divide-y kommandant-divide-gray-100 kommandant-overflow-hidden kommandant-rounded-xl kommandant-bg-white kommandant-shadow-2xl kommandant-ring-1 kommandant-ring-black kommandant-ring-opacity-5 kommandant-transition-all">
|
22
|
+
<%= turbo_frame_tag :command_palette do %>
|
23
|
+
<div data-command-palette-target="content">
|
24
|
+
<div class="kommandant-relative">
|
25
|
+
<div class="kommandant-pointer-events-none kommandant-absolute kommandant-top-3 kommandant-left-4 kommandant-text-gray-400">
|
26
|
+
<%= render "kommandant/shared/icons/search" %>
|
27
|
+
</div>
|
28
|
+
<%= form_with url: Kommandant::Engine.routes.url_helpers.searches_path, method: :get do |form| %>
|
29
|
+
<%= form.text_field :query, value: local_assigns[:query], class: "kommandant-h-12 kommandant-w-full kommandant-border-0 kommandant-bg-transparent kommandant-pl-11 kommandant-pr-4 kommandant-text-gray-800 kommandant-placeholder-gray-400 focus:kommandant-ring-0 sm:kommandant-text-sm", placeholder: t(".input_placeholder"), autocomplete: "off", data: {command_palette_target: "input", action: "keyup->command-palette#submit"} %>
|
30
|
+
<% end %>
|
31
|
+
</div>
|
32
|
+
<% if local_assigns[:results] %>
|
33
|
+
<% if local_assigns[:results].empty? && local_assigns[:query] %>
|
34
|
+
<%= render "kommandant/shared/command_palette/empty_state", query: query %>
|
35
|
+
<% elsif local_assigns[:results].any? %>
|
36
|
+
<!-- Results, show/hide based on command palette state. -->
|
37
|
+
<ul class="kommandant-max-h-96 kommandant-overflow-y-auto kommandant-p-2 kommandant-text-sm kommandant-text-gray-700">
|
38
|
+
<% local_assigns[:results].each do |result| %>
|
39
|
+
<%= render "kommandant/shared/command_palette/command", command: result %>
|
40
|
+
<% end %>
|
41
|
+
</ul>
|
42
|
+
<% end %>
|
43
|
+
<% else %>
|
44
|
+
<%= render "kommandant/shared/command_palette/default_state", recent_commands: local_assigns[:recent_commands] %>
|
45
|
+
<% end %>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
<%= render "kommandant/shared/command_palette/loading_message" %>
|
49
|
+
<% end %>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<li>
|
2
|
+
<%= link_to command.path, class: "kommandant-group kommandant-cursor-default kommandant-select-none kommandant-flex kommandant-items-center kommandant-px-3 kommandant-py-2 kommandant-rounded-md data-[active=true]:kommandant-bg-slate-600 data-[active=true]:kommandant-text-white group-data-[active=true]:kommandant-outline-none", data: {active: false, keyboard_navigation_target: "focusable", action: "mouseenter->keyboard-navigation#focus click->command-palette#showLoadingMessage", turbo_method: command.http_method} do %>
|
3
|
+
<% if command.icon? %>
|
4
|
+
<!-- For some reason the line below works, but the uncommented line does not color the icon... I have no idea why... -->
|
5
|
+
<!-- <div class="kommandant-flex-none kommandant-text-gray-400 group-data-[active=true]:text-white"> -->
|
6
|
+
<div class="kommandant-flex-none kommandant-text-gray-400 group-data-[active=true]:kommandant-text-white">
|
7
|
+
<%= render "kommandant/shared/icons/command", icon: command.icon %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<span class="kommandant-ml-3 kommandant-flex-auto kommandant-truncate"><%= command.name %></span>
|
12
|
+
<span class="kommandant-ml-3 kommandant-hidden group-data[active=true]:kommandant-inline kommandant-flex-none kommandant-text-indigo-100"><%= t("kommandant.navigation.jump_to") %></span>
|
13
|
+
<% end %>
|
14
|
+
</li>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<% if local_assigns[:recent_commands] %>
|
2
|
+
<ul class="kommandant-max-h-80 kommandant-scroll-py-2 kommandant-divide-y kommandant-divide-gray-100 kommandant-overflow-y-auto">
|
3
|
+
<% if recent_commands.any? %>
|
4
|
+
<li class="kommandant-p-2">
|
5
|
+
<h2 class="kommandant-mt-4 kommandant-mb-2 kommandant-px-3 kommandant-text-xs kommandant-font-semibold kommandant-text-gray-500"><%= t(".recent_commands") %></h2>
|
6
|
+
<ul class="kommandant-text-sm kommandant-text-gray-700">
|
7
|
+
<% recent_commands.each do |command| %>
|
8
|
+
<%= render "kommandant/shared/command_palette/command", command: command %>
|
9
|
+
<% end %>
|
10
|
+
</ul>
|
11
|
+
</li>
|
12
|
+
<% end %>
|
13
|
+
</ul>
|
14
|
+
<% end %>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<div class="kommandant-py-14 kommandant-px-6 kommandant-text-center sm:kommandant-px-14">
|
2
|
+
<span class="kommandant-p-2 kommandant-shadow-sm kommandant-inline-flex kommandant-items-center kommandant-justify-center kommandant-text-xs kommandant-leading-5 kommandant-font-semibold kommandant-rounded-full kommandant-flex-shrink-0 kommandant-h-10 kommandant-w-10 kommandant-bg-slate-700 kommandant-text-white">
|
3
|
+
<%= render "kommandant/shared/icons/search" %>
|
4
|
+
</span>
|
5
|
+
|
6
|
+
<p class="kommandant-mt-4 kommandant-text-sm kommandant-text-gray-900"><%= t(".text") %> <span class="kommandant-font-medium"><%= query %></span></p>
|
7
|
+
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<div data-command-palette-target="loadingMessage" class="hidden">
|
2
|
+
<div class="kommandant-flex kommandant-items-center kommandant-space-x-2 kommandant-px-4 kommandant-h-12">
|
3
|
+
<%= render "kommandant/shared/icons/spinner" %>
|
4
|
+
|
5
|
+
<p class="kommandant-text-sm kommandant-font-medium kommandant-text-gray-700"><%= t(".text") %></p>
|
6
|
+
</div>
|
7
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<li>
|
2
|
+
<%= link_to result.path, data: {active: false, keyboard_navigation_target: "focusable", action: "mouseenter->keyboard-navigation#focus click->command-palette#showLoadingMessage", turbo_method: result.http_method, turbo_frame: "_top"}, class: "kommandant-group kommandant-cursor-default kommandant-select-none kommandant-flex kommandant-items-center kommandant-px-3 kommandant-py-2 kommandant-rounded-md data-[active=true]:kommandant-bg-slate-600 data-[active=true]:kommandant-text-white group-data-[active=true]:kommandant-outline-none" do %>
|
3
|
+
<span class="kommandant-ml-3 kommandant-flex-auto kommandant-truncate"><%= result.name %></span>
|
4
|
+
<span class="kommandant-ml-3 kommandant-hidden group-data-[active=true]:kommandant-inline kommandant-flex-none kommandant-text-indigo-100"><%= t("kommandant.navigation.jump_to") %></span>
|
5
|
+
<% end %>
|
6
|
+
</li>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="kommandant-w-6 kommandant-h-6">
|
2
|
+
<path fill-rule="evenodd" d="M16.28 11.47a.75.75 0 010 1.06l-7.5 7.5a.75.75 0 01-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 011.06-1.06l7.5 7.5z" clip-rule="evenodd" />
|
3
|
+
</svg>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<div class="kommandant-flex kommandant-space-x-2">
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="kommandant-w-5 kommandant-h-5">
|
3
|
+
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
|
4
|
+
</svg>
|
5
|
+
|
6
|
+
<span><%= t(".icon_missing", icon_name: icon) %></span>
|
7
|
+
</div>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="kommandant-w-5 kommandant-h-5">
|
2
|
+
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
|
3
|
+
</svg>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg class="kommandant-animate-spin kommandant-h-5 kommandant-w-5 kommandant-text-gray-900" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
2
|
+
<circle class="kommandant-opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
3
|
+
<path class="kommandant-opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
4
|
+
</svg>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
da:
|
2
|
+
kommandant:
|
3
|
+
shared:
|
4
|
+
command_palette:
|
5
|
+
input_placeholder: Søg
|
6
|
+
default_state:
|
7
|
+
recent_commands: Sidst brugte
|
8
|
+
empty_state:
|
9
|
+
text: Vi kunne ikke finde nogen resultater for
|
10
|
+
loading_message:
|
11
|
+
text: Henter...
|
12
|
+
icons:
|
13
|
+
command:
|
14
|
+
icon_missing: Command icon mangler med navn '%{icon_name}'
|
15
|
+
navigation:
|
16
|
+
jump_to: Gå til
|
17
|
+
pagination:
|
18
|
+
prev: Forrige
|
19
|
+
next: Næste
|
@@ -0,0 +1,19 @@
|
|
1
|
+
en:
|
2
|
+
kommandant:
|
3
|
+
shared:
|
4
|
+
command_palette:
|
5
|
+
input_placeholder: Search
|
6
|
+
default_state:
|
7
|
+
recent_commands: Recently used
|
8
|
+
empty_state:
|
9
|
+
text: No results found for
|
10
|
+
loading_message:
|
11
|
+
text: Loading...
|
12
|
+
icons:
|
13
|
+
command:
|
14
|
+
icon_missing: Command icon missing with name '%{icon_name}'
|
15
|
+
navigation:
|
16
|
+
jump_to: Jump to
|
17
|
+
pagination:
|
18
|
+
prev: Previous
|
19
|
+
next: Next
|
data/config/routes.rb
CHANGED
data/lib/kommandant/engine.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
module Kommandant
|
2
2
|
class Engine < ::Rails::Engine
|
3
3
|
isolate_namespace Kommandant
|
4
|
+
|
5
|
+
PRECOMPILE_ASSETS = %w( kommandant.js )
|
6
|
+
initializer "kommandant.assets" do |app|
|
7
|
+
if Rails.application.config.respond_to?(:assets)
|
8
|
+
Rails.application.config.assets.precompile += PRECOMPILE_ASSETS
|
9
|
+
end
|
10
|
+
end
|
4
11
|
end
|
5
12
|
end
|
data/lib/kommandant/version.rb
CHANGED
data/lib/kommandant.rb
CHANGED
@@ -1,6 +1,26 @@
|
|
1
1
|
require "kommandant/version"
|
2
2
|
require "kommandant/engine"
|
3
|
+
require "meilisearch-rails"
|
4
|
+
require "dry-configurable"
|
3
5
|
|
4
6
|
module Kommandant
|
5
|
-
|
7
|
+
extend Dry::Configurable
|
8
|
+
|
9
|
+
setting :commands_path, default: "config/kommandant/commands.json"
|
10
|
+
setting :search_result_filter_lambda
|
11
|
+
setting :admin_only_filter_lambda
|
12
|
+
setting :parent_controller, default: "::ApplicationController"
|
13
|
+
setting :current_user_method, default: "current_user"
|
14
|
+
setting :recent_commands do
|
15
|
+
setting :enabled, default: true
|
16
|
+
end
|
17
|
+
setting :pagination do
|
18
|
+
setting :enabled, default: true
|
19
|
+
setting :items_per_page, default: 10
|
20
|
+
setting :pagination_lambda, default: ->(results, items, controller) { controller.send(:pagy_array, results, items: items) }
|
21
|
+
setting :info_label_lambda, default: ->(pagination, controller) { controller.send(:pagy_info, pagination).html_safe }
|
22
|
+
setting :module, default: "Pagy::Frontend"
|
23
|
+
end
|
24
|
+
|
25
|
+
autoload :Command, "kommandant/command"
|
6
26
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kommandant
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nicolai Bach Woller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,6 +24,68 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 7.0.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: meilisearch-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.8.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.8.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: turbo-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dry-configurable
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 1.0.1
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - "~>"
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '1.0'
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.0.1
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: tailwindcss-rails
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 2.0.30
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.0.30
|
27
89
|
description: A command palette built with Hotwire and Meilisearch
|
28
90
|
email:
|
29
91
|
- woller@traels.it
|
@@ -34,14 +96,38 @@ files:
|
|
34
96
|
- MIT-LICENSE
|
35
97
|
- README.md
|
36
98
|
- Rakefile
|
99
|
+
- app/assets/builds/kommandant.css
|
37
100
|
- app/assets/config/kommandant_manifest.js
|
38
101
|
- app/assets/stylesheets/kommandant/application.css
|
102
|
+
- app/assets/stylesheets/kommandant/application.tailwind.css
|
103
|
+
- app/controllers/concerns/kommandant/recent_commands.rb
|
39
104
|
- app/controllers/kommandant/application_controller.rb
|
105
|
+
- app/controllers/kommandant/commands/searches_controller.rb
|
106
|
+
- app/controllers/kommandant/commands_controller.rb
|
107
|
+
- app/controllers/kommandant/searches_controller.rb
|
40
108
|
- app/helpers/kommandant/application_helper.rb
|
41
109
|
- app/jobs/kommandant/application_job.rb
|
42
110
|
- app/mailers/kommandant/application_mailer.rb
|
43
111
|
- app/models/kommandant/application_record.rb
|
112
|
+
- app/models/kommandant/command.rb
|
113
|
+
- app/models/kommandant/commands/search_result.rb
|
114
|
+
- app/views/kommandant/commands/searches/show.html.erb
|
115
|
+
- app/views/kommandant/commands/show.html.erb
|
116
|
+
- app/views/kommandant/searches/index.html.erb
|
117
|
+
- app/views/kommandant/searches/new.html.erb
|
118
|
+
- app/views/kommandant/shared/_command_palette.html.erb
|
119
|
+
- app/views/kommandant/shared/command_palette/_command.html.erb
|
120
|
+
- app/views/kommandant/shared/command_palette/_default_state.html.erb
|
121
|
+
- app/views/kommandant/shared/command_palette/_empty_state.html.erb
|
122
|
+
- app/views/kommandant/shared/command_palette/_loading_message.html.erb
|
123
|
+
- app/views/kommandant/shared/command_palette/_result.html.erb
|
124
|
+
- app/views/kommandant/shared/icons/_chevron_right.html.erb
|
125
|
+
- app/views/kommandant/shared/icons/_command.erb
|
126
|
+
- app/views/kommandant/shared/icons/_search.erb
|
127
|
+
- app/views/kommandant/shared/icons/_spinner.erb
|
44
128
|
- app/views/layouts/kommandant/application.html.erb
|
129
|
+
- config/locales/da.yml
|
130
|
+
- config/locales/en.yml
|
45
131
|
- config/routes.rb
|
46
132
|
- lib/kommandant.rb
|
47
133
|
- lib/kommandant/engine.rb
|
@@ -68,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
154
|
- !ruby/object:Gem::Version
|
69
155
|
version: '0'
|
70
156
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
157
|
+
rubygems_version: 3.5.6
|
72
158
|
signing_key:
|
73
159
|
specification_version: 4
|
74
160
|
summary: A command palette for Rails
|