feat: preliminary work on assistant
This commit is contained in:
@@ -47,6 +47,8 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
|> assign(:page_language, ShellData.ui_language())
|
|> assign(:page_language, ShellData.ui_language())
|
||||||
|> assign(:client_shortcuts, Commands.client_shortcuts())
|
|> assign(:client_shortcuts, Commands.client_shortcuts())
|
||||||
|> assign(:offline_mode, true)
|
|> assign(:offline_mode, true)
|
||||||
|
|> assign(:assistant_prompt, "")
|
||||||
|
|> assign(:assistant_messages, [])
|
||||||
|> assign(:is_mac_ui, mac_ui?())
|
|> assign(:is_mac_ui, mac_ui?())
|
||||||
|> assign(:menu_groups, titlebar_menu_groups())
|
|> assign(:menu_groups, titlebar_menu_groups())
|
||||||
|> assign(:titlebar_menu_group, nil)
|
|> assign(:titlebar_menu_group, nil)
|
||||||
@@ -275,6 +277,25 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
{:noreply, reload_shell(socket, socket.assigns.workbench)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("update_assistant_prompt", %{"assistant" => %{"prompt" => prompt}}, socket) do
|
||||||
|
{:noreply, assign(socket, :assistant_prompt, prompt)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_event("submit_assistant_prompt", %{"assistant" => %{"prompt" => prompt}}, socket) do
|
||||||
|
prompt = prompt |> to_string() |> String.trim()
|
||||||
|
|
||||||
|
socket =
|
||||||
|
if prompt == "" do
|
||||||
|
assign(socket, :assistant_prompt, "")
|
||||||
|
else
|
||||||
|
socket
|
||||||
|
|> assign(:assistant_prompt, "")
|
||||||
|
|> assign(:assistant_messages, socket.assigns.assistant_messages ++ assistant_turn(prompt, socket))
|
||||||
|
end
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("open_tasks_panel", _params, socket) do
|
def handle_event("open_tasks_panel", _params, socket) do
|
||||||
workbench =
|
workbench =
|
||||||
socket.assigns.workbench
|
socket.assigns.workbench
|
||||||
@@ -1506,6 +1527,34 @@ defmodule BDS.Desktop.ShellLive do
|
|||||||
defp tab_icon_id(%{type: :style}), do: "settings"
|
defp tab_icon_id(%{type: :style}), do: "settings"
|
||||||
defp tab_icon_id(%{type: type}), do: Atom.to_string(type)
|
defp tab_icon_id(%{type: type}), do: Atom.to_string(type)
|
||||||
|
|
||||||
|
defp assistant_turn(prompt, socket) do
|
||||||
|
[
|
||||||
|
%{role: "user", content: prompt},
|
||||||
|
%{role: "assistant", content: assistant_reply(socket)}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assistant_reply(socket) do
|
||||||
|
if socket.assigns.offline_mode do
|
||||||
|
ShellData.translate("Automatic AI actions stay gated by airplane mode.", %{}, socket.assigns.page_language)
|
||||||
|
else
|
||||||
|
ShellData.translate(
|
||||||
|
"The assistant sidebar chat surface is ready, but model execution is not connected yet.",
|
||||||
|
%{},
|
||||||
|
socket.assigns.page_language
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp assistant_project_name(nil), do: translated("Projects")
|
||||||
|
defp assistant_project_name(project), do: project.name
|
||||||
|
|
||||||
|
defp assistant_message_label("assistant"), do: translated("Assistant")
|
||||||
|
defp assistant_message_label("user"), do: translated("You")
|
||||||
|
defp assistant_message_label(_role), do: translated("Assistant")
|
||||||
|
|
||||||
|
defp assistant_message_testid(role), do: "assistant-message-#{role}"
|
||||||
|
|
||||||
defp media_thumbnail_glyph(mime_type) do
|
defp media_thumbnail_glyph(mime_type) do
|
||||||
case String.split(to_string(mime_type || ""), "/", parts: 2) do
|
case String.split(to_string(mime_type || ""), "/", parts: 2) do
|
||||||
["image", _rest] -> "IMG"
|
["image", _rest] -> "IMG"
|
||||||
|
|||||||
@@ -416,10 +416,58 @@
|
|||||||
>
|
>
|
||||||
<div class="resizable-panel-divider assistant-divider" data-resize="assistant" data-role="resize-handle"></div>
|
<div class="resizable-panel-divider assistant-divider" data-resize="assistant" data-role="resize-handle"></div>
|
||||||
<aside class="assistant-sidebar" data-region="assistant-sidebar">
|
<aside class="assistant-sidebar" data-region="assistant-sidebar">
|
||||||
<div class="assistant-header">
|
|
||||||
<strong><%= translated("Assistant") %></strong>
|
|
||||||
</div>
|
|
||||||
<div class="assistant-content">
|
<div class="assistant-content">
|
||||||
|
<header class="assistant-sidebar-header">
|
||||||
|
<div class="assistant-sidebar-heading">
|
||||||
|
<strong><%= translated("AI Assistant") %></strong>
|
||||||
|
<span class="assistant-sidebar-description"><%= translated("AI conversations") %></span>
|
||||||
|
</div>
|
||||||
|
<span class={[
|
||||||
|
"assistant-sidebar-status",
|
||||||
|
if(@offline_mode, do: "is-offline", else: "is-online")
|
||||||
|
]}>
|
||||||
|
<%= if @offline_mode, do: translated("Offline"), else: translated("Chat") %>
|
||||||
|
</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="assistant-sidebar-context" data-testid="assistant-context">
|
||||||
|
<div class="assistant-sidebar-context-row">
|
||||||
|
<span class="assistant-sidebar-context-label"><%= translated("Project") %></span>
|
||||||
|
<span class="assistant-sidebar-context-value"><%= assistant_project_name(@current_project) %></span>
|
||||||
|
</div>
|
||||||
|
<div class="assistant-sidebar-context-row">
|
||||||
|
<span class="assistant-sidebar-context-label"><%= translated("Editor") %></span>
|
||||||
|
<span class="assistant-sidebar-context-value"><%= tab_title(@current_tab, @tab_meta) %></span>
|
||||||
|
</div>
|
||||||
|
<p class="assistant-sidebar-context-text"><%= tab_subtitle(@current_tab, @tab_meta) %></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<form
|
||||||
|
class="assistant-sidebar-prompt-form"
|
||||||
|
data-testid="assistant-prompt-form"
|
||||||
|
phx-change="update_assistant_prompt"
|
||||||
|
phx-submit="submit_assistant_prompt"
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
class="assistant-sidebar-prompt"
|
||||||
|
data-testid="assistant-prompt-input"
|
||||||
|
name="assistant[prompt]"
|
||||||
|
rows="6"
|
||||||
|
placeholder={translated("Ask the assistant about the active project or editor.")}
|
||||||
|
><%= @assistant_prompt %></textarea>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="assistant-sidebar-start-button"
|
||||||
|
data-testid="assistant-start-button"
|
||||||
|
type="submit"
|
||||||
|
disabled={String.trim(@assistant_prompt || "") == ""}
|
||||||
|
>
|
||||||
|
<%= translated("Start chat") %>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<%= if Enum.empty?(@assistant_messages) do %>
|
||||||
|
<div class="assistant-sidebar-welcome">
|
||||||
<%= for card <- @assistant_cards do %>
|
<%= for card <- @assistant_cards do %>
|
||||||
<section class="assistant-card">
|
<section class="assistant-card">
|
||||||
<strong><%= translated(card.label) %></strong>
|
<strong><%= translated(card.label) %></strong>
|
||||||
@@ -427,6 +475,20 @@
|
|||||||
</section>
|
</section>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="assistant-sidebar-transcript">
|
||||||
|
<%= for message <- @assistant_messages do %>
|
||||||
|
<article
|
||||||
|
class={["assistant-sidebar-message", message.role]}
|
||||||
|
data-testid={assistant_message_testid(message.role)}
|
||||||
|
>
|
||||||
|
<span class="assistant-sidebar-message-role"><%= assistant_message_label(message.role) %></span>
|
||||||
|
<p class="assistant-sidebar-message-content"><%= message.content %></p>
|
||||||
|
</article>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -221,5 +221,9 @@
|
|||||||
"View": "Ansicht",
|
"View": "Ansicht",
|
||||||
"Welcome to bDS2": "Willkommen bei bDS2",
|
"Welcome to bDS2": "Willkommen bei bDS2",
|
||||||
"Workbench Notes": "Workbench-Hinweise",
|
"Workbench Notes": "Workbench-Hinweise",
|
||||||
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "Die Integration des Arbeitsverzeichnisses ist in der Shell noch nicht verdrahtet, aber der Tab ist auswählbar und bereit für Befehlsausgaben."
|
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "Die Integration des Arbeitsverzeichnisses ist in der Shell noch nicht verdrahtet, aber der Tab ist auswählbar und bereit für Befehlsausgaben.",
|
||||||
|
"Ask the assistant about the active project or editor.": "Frage den Assistenten zum aktiven Projekt oder Editor.",
|
||||||
|
"Start chat": "Chat starten",
|
||||||
|
"You": "Du",
|
||||||
|
"The assistant sidebar chat surface is ready, but model execution is not connected yet.": "Die Chat-Oberfläche der Assistenten-Seitenleiste ist bereit, aber die Modellausführung ist noch nicht verbunden."
|
||||||
}
|
}
|
||||||
@@ -221,5 +221,9 @@
|
|||||||
"View": "View",
|
"View": "View",
|
||||||
"Welcome to bDS2": "Welcome to bDS2",
|
"Welcome to bDS2": "Welcome to bDS2",
|
||||||
"Workbench Notes": "Workbench Notes",
|
"Workbench Notes": "Workbench Notes",
|
||||||
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output."
|
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.",
|
||||||
|
"Ask the assistant about the active project or editor.": "Ask the assistant about the active project or editor.",
|
||||||
|
"Start chat": "Start chat",
|
||||||
|
"You": "You",
|
||||||
|
"The assistant sidebar chat surface is ready, but model execution is not connected yet.": "The assistant sidebar chat surface is ready, but model execution is not connected yet."
|
||||||
}
|
}
|
||||||
@@ -221,5 +221,9 @@
|
|||||||
"View": "Ver",
|
"View": "Ver",
|
||||||
"Welcome to bDS2": "Bienvenido a bDS2",
|
"Welcome to bDS2": "Bienvenido a bDS2",
|
||||||
"Workbench Notes": "Notas del área de trabajo",
|
"Workbench Notes": "Notas del área de trabajo",
|
||||||
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "La integración del árbol de trabajo aún no está conectada en el shell, pero la pestaña es seleccionable y está lista para la salida de comandos."
|
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "La integración del árbol de trabajo aún no está conectada en el shell, pero la pestaña es seleccionable y está lista para la salida de comandos.",
|
||||||
|
"Ask the assistant about the active project or editor.": "Pregunta al asistente sobre el proyecto o editor activo.",
|
||||||
|
"Start chat": "Iniciar chat",
|
||||||
|
"You": "Tú",
|
||||||
|
"The assistant sidebar chat surface is ready, but model execution is not connected yet.": "La superficie de chat de la barra lateral del asistente está lista, pero la ejecución del modelo aún no está conectada."
|
||||||
}
|
}
|
||||||
@@ -221,5 +221,9 @@
|
|||||||
"View": "Affichage",
|
"View": "Affichage",
|
||||||
"Welcome to bDS2": "Bienvenue dans bDS2",
|
"Welcome to bDS2": "Bienvenue dans bDS2",
|
||||||
"Workbench Notes": "Notes d’atelier",
|
"Workbench Notes": "Notes d’atelier",
|
||||||
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "L’intégration de l’arbre de travail n’est pas encore câblée dans le shell, mais l’onglet est sélectionnable et prêt pour la sortie des commandes."
|
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "L’intégration de l’arbre de travail n’est pas encore câblée dans le shell, mais l’onglet est sélectionnable et prêt pour la sortie des commandes.",
|
||||||
|
"Ask the assistant about the active project or editor.": "Interrogez l’assistant sur le projet ou l’éditeur actif.",
|
||||||
|
"Start chat": "Démarrer la conversation",
|
||||||
|
"You": "Vous",
|
||||||
|
"The assistant sidebar chat surface is ready, but model execution is not connected yet.": "La surface de discussion de la barre latérale de l’assistant est prête, mais l’exécution du modèle n’est pas encore connectée."
|
||||||
}
|
}
|
||||||
@@ -221,5 +221,9 @@
|
|||||||
"View": "Vista",
|
"View": "Vista",
|
||||||
"Welcome to bDS2": "Benvenuto in bDS2",
|
"Welcome to bDS2": "Benvenuto in bDS2",
|
||||||
"Workbench Notes": "Note del banco di lavoro",
|
"Workbench Notes": "Note del banco di lavoro",
|
||||||
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "L’integrazione del working tree non è ancora collegata nella shell, ma la scheda è selezionabile e pronta per l’output dei comandi."
|
"Working tree integration is not wired yet in the shell, but the tab is selectable and ready for command output.": "L’integrazione del working tree non è ancora collegata nella shell, ma la scheda è selezionabile e pronta per l’output dei comandi.",
|
||||||
|
"Ask the assistant about the active project or editor.": "Chiedi all’assistente del progetto o editor attivo.",
|
||||||
|
"Start chat": "Avvia chat",
|
||||||
|
"You": "Tu",
|
||||||
|
"The assistant sidebar chat surface is ready, but model execution is not connected yet.": "La superficie chat della barra laterale assistente è pronta, ma l’esecuzione del modello non è ancora collegata."
|
||||||
}
|
}
|
||||||
128
priv/ui/app.css
128
priv/ui/app.css
@@ -2010,6 +2010,134 @@ button {
|
|||||||
padding: 14px;
|
padding: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-heading {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-description,
|
||||||
|
.assistant-sidebar-context-text,
|
||||||
|
.assistant-sidebar-message-content {
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-status {
|
||||||
|
border-radius: 999px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-status.is-offline {
|
||||||
|
background: rgba(255, 196, 0, 0.18);
|
||||||
|
border-color: rgba(255, 196, 0, 0.35);
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-sideBar-background) 78%, var(--vscode-editor-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-label,
|
||||||
|
.assistant-sidebar-message-role {
|
||||||
|
font-size: 11px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-value {
|
||||||
|
text-align: right;
|
||||||
|
color: var(--vscode-editor-foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-context-text,
|
||||||
|
.assistant-sidebar-message-content {
|
||||||
|
margin: 0;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-prompt-form,
|
||||||
|
.assistant-sidebar-welcome,
|
||||||
|
.assistant-sidebar-transcript {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-prompt {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 112px;
|
||||||
|
resize: vertical;
|
||||||
|
border: 1px solid var(--vscode-input-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--vscode-input-background);
|
||||||
|
color: var(--vscode-input-foreground);
|
||||||
|
padding: 10px 12px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-prompt:focus {
|
||||||
|
outline: 1px solid var(--vscode-focusBorder);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-start-button {
|
||||||
|
align-self: flex-start;
|
||||||
|
border: 1px solid var(--vscode-button-border, transparent);
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--vscode-button-background);
|
||||||
|
color: var(--vscode-button-foreground);
|
||||||
|
padding: 7px 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-start-button:disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-card,
|
||||||
|
.assistant-sidebar-message {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--vscode-panel-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
background: color-mix(in srgb, var(--vscode-sideBar-background) 82%, var(--vscode-editor-background));
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-message.user {
|
||||||
|
background: color-mix(in srgb, var(--vscode-list-hoverBackground) 76%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.assistant-sidebar-message.assistant {
|
||||||
|
background: color-mix(in srgb, var(--vscode-sideBar-background) 70%, var(--vscode-editor-background));
|
||||||
|
}
|
||||||
|
|
||||||
.status-bar {
|
.status-bar {
|
||||||
height: 22px;
|
height: 22px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -177,6 +177,32 @@ defmodule BDS.Desktop.ShellLiveTest do
|
|||||||
assert html =~ ">7<"
|
assert html =~ ">7<"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "assistant sidebar exposes context, prompt, and offline-gated transcript" do
|
||||||
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
html =
|
||||||
|
view
|
||||||
|
|> element("[data-testid='toggle-assistant']")
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert html =~ ~s(data-testid="assistant-shell")
|
||||||
|
assert html =~ ~s(data-testid="assistant-context")
|
||||||
|
assert html =~ ~s(data-testid="assistant-prompt-form")
|
||||||
|
assert html =~ ~s(data-testid="assistant-prompt-input")
|
||||||
|
assert html =~ ~s(data-testid="assistant-start-button")
|
||||||
|
assert html =~ ~s(>Dashboard<)
|
||||||
|
|
||||||
|
html =
|
||||||
|
render_submit(view, "submit_assistant_prompt", %{
|
||||||
|
"assistant" => %{"prompt" => "Summarize the current project"}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert html =~ ~s(data-testid="assistant-message-user")
|
||||||
|
assert html =~ ~s(data-testid="assistant-message-assistant")
|
||||||
|
assert html =~ "Summarize the current project"
|
||||||
|
assert html =~ "Automatic AI actions stay gated by airplane mode."
|
||||||
|
end
|
||||||
|
|
||||||
test "sidebar open supports preview and pin intents for entity tabs" do
|
test "sidebar open supports preview and pin intents for entity tabs" do
|
||||||
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
{:ok, view, _html} = live_isolated(build_conn(), BDS.Desktop.ShellLive)
|
||||||
|
|
||||||
|
|||||||
@@ -160,4 +160,19 @@ defmodule BDS.UI.ShellTest do
|
|||||||
assert template =~ "tab-actions"
|
assert template =~ "tab-actions"
|
||||||
assert template =~ "tab-dirty-indicator"
|
assert template =~ "tab-dirty-indicator"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "desktop shell assets keep the assistant sidebar chat surface contract" do
|
||||||
|
css = File.read!("/Users/gb/Projects/bDS2/priv/ui/app.css")
|
||||||
|
template = File.read!("/Users/gb/Projects/bDS2/lib/bds/desktop/shell_live/index.html.heex")
|
||||||
|
|
||||||
|
assert css =~ ".assistant-sidebar-context"
|
||||||
|
assert css =~ ".assistant-sidebar-prompt"
|
||||||
|
assert css =~ ".assistant-sidebar-start-button"
|
||||||
|
assert css =~ ".assistant-sidebar-message"
|
||||||
|
assert template =~ "data-testid=\"assistant-context\""
|
||||||
|
assert template =~ "data-testid=\"assistant-prompt-form\""
|
||||||
|
assert template =~ "data-testid=\"assistant-prompt-input\""
|
||||||
|
assert template =~ "data-testid=\"assistant-start-button\""
|
||||||
|
assert template =~ "assistant-sidebar-transcript"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user