overlay for post suggestions uses AI now
This commit is contained in:
@@ -228,6 +228,26 @@ defmodule BDS.Desktop.Overlay do
|
||||
}
|
||||
end
|
||||
|
||||
def set_ai_suggestions(%{kind: :ai_suggestions} = overlay, suggestions) do
|
||||
fields =
|
||||
Enum.map(overlay.fields, fn field ->
|
||||
case Map.get(suggestions, field.key) do
|
||||
nil ->
|
||||
field
|
||||
|
||||
value when is_binary(value) and value != "" ->
|
||||
%{field | suggested_value: value, loading: false}
|
||||
|
||||
_other ->
|
||||
field
|
||||
end
|
||||
end)
|
||||
|
||||
%{overlay | fields: fields}
|
||||
end
|
||||
|
||||
def set_ai_suggestions(overlay, _suggestions), do: overlay
|
||||
|
||||
defp normalize_ai_fields(fields) do
|
||||
Enum.map(fields, fn field ->
|
||||
%{
|
||||
@@ -236,7 +256,8 @@ defmodule BDS.Desktop.Overlay do
|
||||
current_value: Map.get(field, :current_value, ""),
|
||||
suggested_value: Map.get(field, :suggested_value, ""),
|
||||
accepted: not Map.get(field, :locked, false),
|
||||
locked: Map.get(field, :locked, false)
|
||||
locked: Map.get(field, :locked, false),
|
||||
loading: Map.get(field, :loading, false)
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
@@ -717,7 +717,27 @@ defmodule BDS.Desktop.ShellLive do
|
||||
)
|
||||
end
|
||||
|
||||
{:noreply, assign(socket, :shell_overlay, overlay)}
|
||||
socket = assign(socket, :shell_overlay, overlay)
|
||||
|
||||
socket =
|
||||
if kind == "ai_suggestions" and not is_nil(overlay) do
|
||||
if socket.assigns.offline_mode do
|
||||
socket
|
||||
|> assign(:shell_overlay, nil)
|
||||
|> append_output_entry(
|
||||
translated("AI Suggestions"),
|
||||
translated("Automatic AI actions stay gated by airplane mode."),
|
||||
nil,
|
||||
"info"
|
||||
)
|
||||
else
|
||||
spawn_ai_suggestions_task(socket)
|
||||
end
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_event("close_overlay", _params, socket) do
|
||||
@@ -1222,6 +1242,68 @@ defmodule BDS.Desktop.ShellLive do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({:ai_suggestions_result, type, id, result}, socket) do
|
||||
socket =
|
||||
case socket.assigns[:shell_overlay] do
|
||||
%{kind: :ai_suggestions} = overlay ->
|
||||
current_tab = socket.assigns.current_tab
|
||||
|
||||
if current_tab && current_tab.type == type && current_tab.id == id do
|
||||
suggestions =
|
||||
case type do
|
||||
:post ->
|
||||
%{
|
||||
"title" => result.title,
|
||||
"excerpt" => result.excerpt,
|
||||
"slug" => result.slug
|
||||
}
|
||||
|
||||
:media ->
|
||||
%{
|
||||
"title" => result.title,
|
||||
"alt" => result.alt,
|
||||
"caption" => result.caption
|
||||
}
|
||||
end
|
||||
|
||||
assign(socket, :shell_overlay, Overlay.set_ai_suggestions(overlay, suggestions))
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
_other ->
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info({:ai_suggestions_error, type, id, reason}, socket) do
|
||||
socket =
|
||||
case socket.assigns[:shell_overlay] do
|
||||
%{kind: :ai_suggestions} ->
|
||||
current_tab = socket.assigns.current_tab
|
||||
|
||||
if current_tab && current_tab.type == type && current_tab.id == id do
|
||||
socket
|
||||
|> assign(:shell_overlay, nil)
|
||||
|> append_output_entry(
|
||||
translated("AI Suggestions"),
|
||||
inspect(reason),
|
||||
nil,
|
||||
"error"
|
||||
)
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
_other ->
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
def handle_info(:reload_shell, socket) do
|
||||
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
||||
end
|
||||
@@ -1564,6 +1646,43 @@ defmodule BDS.Desktop.ShellLive do
|
||||
|
||||
defp shell_command_atom(action), do: ShellCommandRunner.shell_command_atom(action)
|
||||
|
||||
defp spawn_ai_suggestions_task(socket) do
|
||||
current_tab = socket.assigns.current_tab
|
||||
|
||||
case current_tab do
|
||||
%{type: :post, id: post_id} ->
|
||||
parent = self()
|
||||
|
||||
Task.Supervisor.start_child(BDS.TCP.TaskSupervisor, fn ->
|
||||
case AI.analyze_post(post_id) do
|
||||
{:ok, result} ->
|
||||
send(parent, {:ai_suggestions_result, :post, post_id, result})
|
||||
|
||||
{:error, reason} ->
|
||||
send(parent, {:ai_suggestions_error, :post, post_id, reason})
|
||||
end
|
||||
end)
|
||||
|
||||
%{type: :media, id: media_id} ->
|
||||
parent = self()
|
||||
|
||||
Task.Supervisor.start_child(BDS.TCP.TaskSupervisor, fn ->
|
||||
case AI.analyze_image(media_id) do
|
||||
{:ok, result} ->
|
||||
send(parent, {:ai_suggestions_result, :media, media_id, result})
|
||||
|
||||
{:error, reason} ->
|
||||
send(parent, {:ai_suggestions_error, :media, media_id, reason})
|
||||
end
|
||||
end)
|
||||
|
||||
_other ->
|
||||
:ok
|
||||
end
|
||||
|
||||
socket
|
||||
end
|
||||
|
||||
defp mac_ui? do
|
||||
case Application.get_env(:bds, :shell_platform) do
|
||||
nil -> match?({:unix, :darwin}, :os.type())
|
||||
|
||||
@@ -219,22 +219,25 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
key: "title",
|
||||
label: ShellData.translate("Title", %{}, page_language),
|
||||
current_value: post.title || title,
|
||||
suggested_value: refine_title(post.title || title),
|
||||
locked: false
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
loading: true
|
||||
},
|
||||
%{
|
||||
key: "excerpt",
|
||||
label: ShellData.translate("Excerpt", %{}, page_language),
|
||||
current_value: post.excerpt || subtitle,
|
||||
suggested_value: refine_excerpt(post.title || title, post.excerpt || subtitle),
|
||||
locked: false
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
loading: true
|
||||
},
|
||||
%{
|
||||
key: "slug",
|
||||
label: ShellData.translate("Slug", %{}, page_language),
|
||||
current_value: post.slug || slugify(post.title || title),
|
||||
suggested_value: refine_slug(post.slug || slugify(post.title || title)),
|
||||
locked: post.status == :published
|
||||
suggested_value: "",
|
||||
locked: post.status == :published,
|
||||
loading: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -253,22 +256,25 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
key: "title",
|
||||
label: ShellData.translate("Title", %{}, page_language),
|
||||
current_value: media.title || title,
|
||||
suggested_value: refine_title(media.title || title),
|
||||
locked: false
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
loading: true
|
||||
},
|
||||
%{
|
||||
key: "alt",
|
||||
label: ShellData.translate("Alt Text", %{}, page_language),
|
||||
current_value: media.alt || "",
|
||||
suggested_value: media.alt || title,
|
||||
locked: false
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
loading: true
|
||||
},
|
||||
%{
|
||||
key: "caption",
|
||||
label: ShellData.translate("Caption", %{}, page_language),
|
||||
current_value: media.caption || "",
|
||||
suggested_value: refine_excerpt(title, media.caption || title),
|
||||
locked: false
|
||||
suggested_value: "",
|
||||
locked: false,
|
||||
loading: true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -381,17 +387,6 @@ defmodule BDS.Desktop.ShellLive.OverlayComponents do
|
||||
|
||||
defp pad2(value), do: value |> Integer.to_string() |> String.pad_leading(2, "0")
|
||||
|
||||
defp refine_title(nil), do: ""
|
||||
defp refine_title(title), do: String.trim(title <> " Notes")
|
||||
|
||||
defp refine_excerpt(title, excerpt) do
|
||||
base = excerpt |> to_string() |> String.trim()
|
||||
if base == "", do: "#{title} overview", else: base <> "."
|
||||
end
|
||||
|
||||
defp refine_slug(slug),
|
||||
do: slug |> to_string() |> String.trim_trailing("-") |> Kernel.<>("-updated")
|
||||
|
||||
defp slugify(value) do
|
||||
value
|
||||
|> to_string()
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={field.accepted}
|
||||
disabled={field.locked}
|
||||
disabled={field.locked or field.loading}
|
||||
phx-click="overlay_toggle_ai_field"
|
||||
phx-value-key={field.key}
|
||||
/>
|
||||
@@ -24,8 +24,9 @@
|
||||
</label>
|
||||
<div class="ai-suggestion-content">
|
||||
<div class="ai-suggestion-label"><%= field.label %></div>
|
||||
<div class="ai-suggestion-current"><%= field.current_value %></div>
|
||||
<div class="ai-suggestion-value"><%= field.suggested_value %></div>
|
||||
<div class={["ai-suggestion-value", if(field.loading, do: "loading")]}>
|
||||
<%= if field.loading, do: "Loading…", else: field.suggested_value %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
Reference in New Issue
Block a user