fix: fixes for AI chat

This commit is contained in:
2026-05-01 20:22:12 +02:00
parent 8a582ee6c7
commit dd0c05b785
8 changed files with 360 additions and 66 deletions

View File

@@ -90,8 +90,8 @@ defmodule BDS.Desktop.MainWindow do
end
@impl true
def terminate(_reason, %{frame: frame, last_bounds: last_bounds}) do
if bounds = current_bounds(frame) || last_bounds do
def terminate(_reason, %{last_bounds: last_bounds}) do
if bounds = last_bounds do
_ = persist_bounds(bounds)
end

View File

@@ -1,54 +1,56 @@
<div id={"chat-editor-#{@chat_editor.id}"} class="chat-panel" data-testid="chat-editor" phx-hook="ChatSurface">
<div class="chat-panel-header">
<div class="chat-panel-title">
<%= if @chat_editor.needs_api_key? do %>
<%= translated("chat.setupTitle") %>
<% else %>
<%= @chat_editor.title %>
<span class="chat-panel-title-main">
<%= if @chat_editor.needs_api_key? do %>
<%= translated("chat.setupTitle") %>
<% else %>
<%= @chat_editor.title %>
<% end %>
</span>
<%= unless @chat_editor.needs_api_key? do %>
<span class="chat-model-selector-wrap">
<button
class="chat-model-selector-button chat-model-selector-inline"
type="button"
phx-click="toggle_chat_model_selector"
data-testid="chat-model-selector-button"
>
<span><%= @chat_editor.model || translated("chat.newChat") %></span>
<span class="chat-model-selector-caret">▾</span>
</button>
<%= if @chat_editor.model_selector_open? and @chat_editor.available_models != [] do %>
<div class="chat-model-selector-menu">
<%= for group <- @chat_editor.available_model_groups do %>
<section class="chat-model-provider-group" data-testid="chat-model-provider-group" data-provider={group.provider}>
<%= if length(@chat_editor.available_model_groups) > 1 do %>
<div class="chat-model-provider-header"><%= group.label %></div>
<% end %>
<%= for model <- group.models do %>
<button
class={[
"chat-model-selector-option",
if(model.id == @chat_editor.model, do: "active")
]}
type="button"
phx-click="select_chat_model"
phx-value-model={model.id}
data-testid="chat-model-selector-option"
data-provider={group.provider}
>
<span class="chat-model-selector-option-name"><%= model.name || model.id %></span>
</button>
<% end %>
</section>
<% end %>
</div>
<% end %>
</span>
<% end %>
</div>
<%= unless @chat_editor.needs_api_key? do %>
<div class="chat-panel-header-actions">
<button
class="chat-model-selector-button"
type="button"
phx-click="toggle_chat_model_selector"
data-testid="chat-model-selector-button"
>
<span><%= @chat_editor.model || translated("chat.newChat") %></span>
<span class="chat-model-selector-caret">▾</span>
</button>
<%= if @chat_editor.model_selector_open? and @chat_editor.available_models != [] do %>
<div class="chat-model-selector-menu">
<%= for group <- @chat_editor.available_model_groups do %>
<section class="chat-model-provider-group" data-testid="chat-model-provider-group" data-provider={group.provider}>
<%= if length(@chat_editor.available_model_groups) > 1 do %>
<div class="chat-model-provider-header"><%= group.label %></div>
<% end %>
<%= for model <- group.models do %>
<button
class={[
"chat-model-selector-option",
if(model.id == @chat_editor.model, do: "active")
]}
type="button"
phx-click="select_chat_model"
phx-value-model={model.id}
data-testid="chat-model-selector-option"
data-provider={group.provider}
>
<span class="chat-model-selector-option-name"><%= model.name || model.id %></span>
</button>
<% end %>
</section>
<% end %>
</div>
<% end %>
</div>
<% end %>
</div>
<div class="chat-messages chat-surface-scroll">
@@ -83,7 +85,7 @@
<div class="chat-message-header">
<span class="chat-message-role"><%= message_role_label(:user) %></span>
</div>
<div class="chat-message-text"><%= @chat_editor.pending_user_message %></div>
<div class="chat-message-text chat-user-message-text" data-testid="chat-user-message-text"><%= @chat_editor.pending_user_message %></div>
</div>
</div>
<% end %>
@@ -95,13 +97,11 @@
<div class="chat-message-header"><span class="chat-message-role"><%= message_role_label(message.role) %></span></div>
<.chat_tool_markers markers={message.tool_markers} />
<div class="chat-message-text">
<%= if message.role == :assistant do %>
<%= markdown_html(message.content || "") %>
<% else %>
<%= message.content || "" %>
<% end %>
</div>
<%= if message.role == :assistant do %>
<div class="chat-message-text"><%= markdown_html(message.content || "") %></div>
<% else %>
<div class="chat-message-text chat-user-message-text" data-testid="chat-user-message-text"><%= message.content || "" %></div>
<% end %>
</div>
</div>

View File

@@ -1,4 +1,11 @@
<div class="settings-view-shell" data-testid="settings-editor" data-selected-settings-section={@settings_editor.selected_section}>
<div
id="settings-editor-shell"
class="settings-view-shell"
data-testid="settings-editor"
phx-hook="SettingsSectionScroll"
data-selected-settings-section={@settings_editor.selected_section}
data-settings-scroll-target={"settings-section-#{@settings_editor.selected_section}"}
>
<div class="settings-view">
<div class="settings-header">
<h2 data-testid="editor-title"><%= translated("Settings") %></h2>

View File

@@ -2,6 +2,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
@moduledoc false
alias BDS.Desktop.{FilePicker, ShellData}
alias BDS.AI
alias BDS.ImportDefinitions
alias BDS.Scripts
alias BDS.Templates
@@ -132,6 +133,27 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
end
end
def create(socket, _project_id, "chat", callbacks) do
case AI.start_chat(%{}) do
{:ok, conversation} ->
callbacks.open_sidebar.(
socket,
%{
"route" => "chat",
"id" => conversation.id,
"title" => conversation.title,
"subtitle" => "AI conversations"
},
:pin
)
{:error, reason} ->
socket
|> callbacks.append_output.(translated("chat.newChat"), inspect(reason), nil, "error")
|> callbacks.reload.(socket.assigns.workbench)
end
end
def create(socket, project_id, "import", callbacks) do
case ImportDefinitions.create_definition(%{
project_id: project_id,
@@ -168,6 +190,7 @@ defmodule BDS.Desktop.ShellLive.SidebarCreate do
def action(:media), do: %{kind: "media", label: "sidebar.importMedia"}
def action(:scripts), do: %{kind: "script", label: "sidebar.scripts.newScript"}
def action(:templates), do: %{kind: "template", label: "sidebar.templates.newTemplate"}
def action(:chat), do: %{kind: "chat", label: "chat.newChat"}
def action(:import), do: %{kind: "import", label: "sidebar.import.newDefinition"}
def action(_view), do: nil