Current section
Files
Jump to
Current section
Files
lib/mix/tasks/command_k.init.ex
defmodule Mix.Tasks.CommandK.Init do
use Mix.Task
@shortdoc "Generates boilerplate code for a Command+K palette"
def generator_paths(), do: [".", :command_k]
@impl true
def run(args) do
if args != [] do
Mix.raise("mix command_k.init does not accept any arguments")
end
if Mix.Project.umbrella?() do
Mix.raise(
"mix command_k.init must be invoked from within your *_web application root directory"
)
end
ctx_app = Mix.Phoenix.context_app()
base = Mix.Phoenix.base()
web_module = Mix.Phoenix.web_module(base)
binding = [assigns: [web_module: web_module]]
commands_file = Mix.Phoenix.web_path(ctx_app, "commands.ex")
core_components_file = Mix.Phoenix.web_path(ctx_app, "components/core_components.ex")
Mix.Phoenix.copy_from(generator_paths(), "priv/templates/command_k.init", binding, [
{:eex, "commands.ex", commands_file}
])
inject_eex_before_final_end(
Mix.Phoenix.eval_from(
generator_paths(),
"priv/templates/command_k.init/core_components_command_k_palette.ex",
binding
),
core_components_file
)
Mix.shell().info("""
Configure CommandK in the `live_view` section in your #{inspect(web_module)} module:
def live_view do
quote do
use Phoenix.LiveView,
layout: {#{inspect(web_module)}.Layouts, :app}
use CommandK,
handler: #{inspect(web_module)}.Commands,
component: &#{inspect(web_module)}.CoreComponents.command_k_palette/1
unquote(html_helpers())
end
end
Then, modify your assets/app.js code to capture meta key information on keydown:
let liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken},
metadata: {
keydown: (e, el) => {
return {
key: e.key,
metaKey: e.metaKey,
repeat: e.repeat
}
}
}
})
Lastly, render the CommandK live component in your "app.html.heex" layout template:
<main class="...">
<div class="...">
<.live_component
id="command_k"
module={CommandK.LiveComponent}
command_k={@command_k}
command_k_context={@command_k_context}
show={@command_k_show}
on_cancel={CommandK.close()}
/>
...
</div>
</main>
""")
end
defp inject_eex_before_final_end(content_to_inject, file_path) do
file = File.read!(file_path)
if String.contains?(file, content_to_inject) do
:ok
else
Mix.shell().info([:green, "* injecting ", :reset, Path.relative_to_cwd(file_path)])
file
|> String.trim_trailing()
|> String.trim_trailing("end")
|> Kernel.<>(content_to_inject)
|> Kernel.<>("end\n")
|> then(&File.write!(file_path, &1))
end
end
end