Files
bDS2/lib/bds/scripting/capabilities/app_shell.ex

171 lines
4.5 KiB
Elixir

defmodule BDS.Scripting.Capabilities.AppShell do
@moduledoc false
import BDS.Scripting.Capabilities.Util
alias BDS.Desktop.FolderPicker
alias BDS.Desktop.MenuBar
require Logger
@compiled_env Application.compile_env(:bds, :current_env, Mix.env())
def copy_to_clipboard(text, opts) do
case Keyword.get(opts, :copy_to_clipboard) do
callback when is_function(callback, 1) -> callback.(string_or_nil(text) || "")
_other -> do_copy_to_clipboard(text)
end
end
defp do_copy_to_clipboard(text) do
if test_mode?() do
true
else
{executable, args} = clipboard_command()
pipe_to_clipboard(executable, args, string_or_nil(text) || "")
end
rescue
_error -> false
end
defp clipboard_command do
case :os.type() do
{:unix, :darwin} -> {"pbcopy", []}
{:win32, _other} -> {"cmd", ["/c", "clip"]}
{:unix, _other} -> {"xclip", ["-selection", "clipboard"]}
end
end
# System.cmd has no stdin option, so feed the clipboard tool through a Port.
# Closing the port signals EOF, which is what pbcopy/xclip/clip wait for.
defp pipe_to_clipboard(executable, args, input) do
case System.find_executable(executable) do
nil ->
false
path ->
port = Port.open({:spawn_executable, path}, [:binary, :use_stdio, args: args])
Port.command(port, input)
Port.close(port)
true
end
end
def blogmark_bookmarklet(opts \\ []) do
project_id = Keyword.get(opts, :project_id)
# Concatenate as a JS string literal (+'&project_id=...'), not raw text, so
# the generated bookmarklet stays valid JavaScript when a project is set.
project_param =
if is_binary(project_id) and project_id != "" do
"+'&project_id=" <> URI.encode_www_form(project_id) <> "'"
else
""
end
"javascript:(()=>{const t=encodeURIComponent(document.title||'');const u=encodeURIComponent(location.href||'');location.href='bds2://new-post?title='+t+'&url='+u#{project_param};})();"
end
def title_bar_metrics(opts) do
case Keyword.get(opts, :title_bar_metrics) do
callback when is_function(callback, 0) -> callback.()
_other -> do_title_bar_metrics()
end
end
defp do_title_bar_metrics do
case :os.type() do
{:unix, :darwin} -> %{macos_left_inset: 72}
_other -> nil
end
end
def notify_renderer_ready(opts) do
case Keyword.get(opts, :notify_renderer_ready) do
callback when is_function(callback, 0) -> callback.()
_other -> true
end
end
def open_folder(folder_path, opts) do
case Keyword.get(opts, :open_folder) do
callback when is_function(callback, 1) -> callback.(string_or_nil(folder_path))
_other -> do_open_folder(folder_path)
end
end
defp do_open_folder(folder_path) do
if test_mode?() do
""
else
case shell_open_system_path(string_or_nil(folder_path)) do
:ok -> ""
{:error, reason} -> inspect(reason)
end
end
end
def select_folder(title, opts) do
case Keyword.get(opts, :select_folder) do
callback when is_function(callback, 1) -> callback.(string_or_nil(title) || "Select Folder")
_other -> do_select_folder(title)
end
end
defp do_select_folder(title) do
if test_mode?() do
nil
else
case FolderPicker.choose_directory(string_or_nil(title) || "Select Folder") do
{:ok, path} -> path
:cancel -> nil
{:error, _reason} -> nil
end
end
end
def set_preview_post_target(post_id) do
:persistent_term.put(
{BDS.Scripting.Capabilities, :preview_post_target},
string_or_nil(post_id)
)
true
end
def show_item_in_folder(item_path, opts) do
callback = Keyword.get(opts, :show_item_in_folder)
cond do
is_function(callback, 1) -> callback.(string_or_nil(item_path))
test_mode?() -> :ok
true -> _ = shell_reveal_system_path(string_or_nil(item_path))
end
nil
end
def trigger_menu_action(action, opts) do
callback = Keyword.get(opts, :trigger_menu_action)
cond do
is_function(callback, 1) -> callback.(string_or_nil(action))
test_mode?() -> :ok
true -> _ = MenuBar.handle_event(string_or_nil(action), nil)
end
nil
rescue
error ->
Logger.debug("swallowed app_shell trigger_menu_action error: #{inspect(error)}")
nil
end
def test_mode? do
Application.get_env(:bds, :test_mode, false) or current_env() == :test
end
def current_env do
Application.get_env(:bds, :current_env_override) || @compiled_env
end
end