feat: added blog selection for existing blog

This commit is contained in:
2026-04-24 18:56:05 +02:00
parent 20ed1348ad
commit 58a332c7c4
12 changed files with 253 additions and 28 deletions

View File

@@ -0,0 +1,35 @@
defmodule BDS.Desktop.FolderPicker do
@moduledoc false
def choose_directory(prompt) when is_binary(prompt) do
case :os.type() do
{:unix, :darwin} -> choose_directory_macos(prompt)
_other -> {:error, %{message: "Folder selection is only supported on macOS desktop"}}
end
end
defp choose_directory_macos(prompt) do
script = "POSIX path of (choose folder with prompt \"#{escape_applescript(prompt)}\")"
case System.cmd("osascript", ["-e", script], stderr_to_stdout: true) do
{output, 0} -> {:ok, String.trim(output)}
{output, _status} -> normalize_picker_failure(output)
end
end
defp normalize_picker_failure(output) do
message = String.trim(output)
if message == "" or String.contains?(String.downcase(message), "canceled") do
:cancel
else
{:error, %{message: message}}
end
end
defp escape_applescript(value) do
value
|> String.replace("\\", "\\\\")
|> String.replace("\"", "\\\"")
end
end

View File

@@ -51,6 +51,15 @@ defmodule BDS.Desktop.Router do
|> Plug.Conn.send_resp(200, BDS.Desktop.ShellController.upsert_project_json(payload))
end
post "/api/project-folder" do
{:ok, body, conn} = Plug.Conn.read_body(conn)
payload = if body == "", do: %{}, else: Jason.decode!(body)
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> Plug.Conn.send_resp(200, BDS.Desktop.ShellController.choose_project_folder_json(payload))
end
post "/api/commands" do
{:ok, body, conn} = Plug.Conn.read_body(conn)
payload = if body == "", do: %{}, else: Jason.decode!(body)

View File

@@ -28,6 +28,16 @@ defmodule BDS.Desktop.ShellController do
end
end
def choose_project_folder_json(payload \\ %{}) when is_map(payload) do
prompt = Map.get(payload, "prompt") || Map.get(payload, :prompt) || "Select existing blog folder"
case folder_picker().choose_directory(prompt) do
{:ok, path} -> Jason.encode!(project_folder_payload(path))
:cancel -> Jason.encode!(%{status: "cancel"})
{:error, error} -> Jason.encode!(%{status: "error", error: normalize_error(error)})
end
end
def command_json(payload) when is_map(payload) do
action = Map.get(payload, "action") || Map.get(payload, :action)
params = Map.get(payload, "params") || Map.get(payload, :params) || %{}
@@ -47,6 +57,7 @@ defmodule BDS.Desktop.ShellController do
{:create,
%{
name: String.trim(Map.get(payload, "name") || Map.get(payload, :name)),
description: blank_to_nil(Map.get(payload, "description") || Map.get(payload, :description)),
data_path: blank_to_nil(Map.get(payload, "data_path") || Map.get(payload, :data_path))
}}
@@ -84,6 +95,45 @@ defmodule BDS.Desktop.ShellController do
%{id: project.id, name: project.name, slug: project.slug, data_path: project.data_path, is_active: project.is_active}
end
defp project_folder_payload(path) do
normalized_path = Path.expand(path)
project_metadata = read_project_metadata(normalized_path)
existing_project = find_project_by_data_path(normalized_path)
%{
status: "ok",
path: normalized_path,
name: Map.get(project_metadata, "name") || Path.basename(normalized_path),
description: Map.get(project_metadata, "description"),
existing_project_id: existing_project && existing_project.id
}
end
defp read_project_metadata(path) do
project_json_path = Path.join([path, "meta", "project.json"])
case File.read(project_json_path) do
{:ok, contents} -> Jason.decode!(contents)
{:error, :enoent} -> %{}
end
end
defp find_project_by_data_path(path) do
normalized_path = Path.expand(path)
BDS.Projects.list_projects()
|> Enum.find(fn project ->
case project.data_path do
value when is_binary(value) -> Path.expand(value) == normalized_path
_other -> false
end
end)
end
defp folder_picker do
Application.get_env(:bds, :desktop, [])[:folder_picker] || BDS.Desktop.FolderPicker
end
defp default_project_snapshot do
%{
active_project_id: "default",