Enable chat for artists
This commit is contained in:
+6
-4
@@ -117,7 +117,9 @@ Last.fm schemas (separate, not Ecto-persisted to main DB):
|
|||||||
| `Assets.Image` / `Assets.Transform` | Image processing via Vix (libvips) |
|
| `Assets.Image` / `Assets.Transform` | Image processing via Vix (libvips) |
|
||||||
| `Colors.ColorFrequencyExtractor` | Color extraction via pixel sampling/histogram |
|
| `Colors.ColorFrequencyExtractor` | Color extraction via pixel sampling/histogram |
|
||||||
| `Colors.EdgeWeightedExtractor` | Color extraction weighted by Sobel edge detection |
|
| `Colors.EdgeWeightedExtractor` | Color extraction weighted by Sobel edge detection |
|
||||||
| `RecordChat` | OpenAI-powered streaming chat about a record (web search enabled) |
|
| `Chat` | Behaviour for streaming AI chat (`stream_response/3` callback) |
|
||||||
|
| `RecordChat` | Chat implementation for records (OpenAI streaming, web search enabled) |
|
||||||
|
| `ArtistChat` | Chat implementation for artists (OpenAI streaming, uses Wikipedia/artist context) |
|
||||||
| `FormatNumber` | Number formatting utility |
|
| `FormatNumber` | Number formatting utility |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -240,7 +242,7 @@ All authenticated routes live inside a single `live_session` with three `on_moun
|
|||||||
| `StatsLive.TopAlbums` | StatsLive.Index | Top albums by period (assign_async) |
|
| `StatsLive.TopAlbums` | StatsLive.Index | Top albums by period (assign_async) |
|
||||||
| `StatsLive.TopArtists` | StatsLive.Index | Top artists by period (assign_async) |
|
| `StatsLive.TopArtists` | StatsLive.Index | Top artists by period (assign_async) |
|
||||||
| `UniversalSearchLive.Index` | Layout (global) | Cmd+K search modal |
|
| `UniversalSearchLive.Index` | Layout (global) | Cmd+K search modal |
|
||||||
| `RecordChat` | CollectionLive.Show, WishlistLive.Show | AI chat sheet for a record (OpenAI streaming) |
|
| `Chat` | CollectionLive.Show, WishlistLive.Show, ArtistLive.Show | AI chat sheet (OpenAI streaming, configurable per entity) |
|
||||||
|
|
||||||
### Shared Component Modules (lib/music_library_web/components/)
|
### Shared Component Modules (lib/music_library_web/components/)
|
||||||
|
|
||||||
@@ -257,7 +259,7 @@ All authenticated routes live inside a single `live_session` with three `on_moun
|
|||||||
| `BarcodeScanner` | Barcode scanning UI (uses barcode-detector JS) |
|
| `BarcodeScanner` | Barcode scanning UI (uses barcode-detector JS) |
|
||||||
| `Release` | MusicBrainz release display |
|
| `Release` | MusicBrainz release display |
|
||||||
| `Notes` | Markdown note rendering |
|
| `Notes` | Markdown note rendering |
|
||||||
| `RecordChat` | AI record chat sheet with streaming responses |
|
| `Chat` | AI chat sheet with streaming responses (generic, used for records and artists) |
|
||||||
| `Markdown` | Markdown-to-HTML conversion with `[[double bracket]]` link syntax |
|
| `Markdown` | Markdown-to-HTML conversion with `[[double bracket]]` link syntax |
|
||||||
| `Duration` | Milliseconds to human-readable duration formatting |
|
| `Duration` | Milliseconds to human-readable duration formatting |
|
||||||
|
|
||||||
@@ -288,7 +290,7 @@ All authenticated routes live inside a single `live_session` with three `on_moun
|
|||||||
| `UniversalSearchNavigation` | External | Keyboard navigation in search modal (via `create-navigation-hook` factory) |
|
| `UniversalSearchNavigation` | External | Keyboard navigation in search modal (via `create-navigation-hook` factory) |
|
||||||
| `RecordPickerNavigation` | External | Keyboard navigation in record picker (via `create-navigation-hook` factory) |
|
| `RecordPickerNavigation` | External | Keyboard navigation in record picker (via `create-navigation-hook` factory) |
|
||||||
| `SortableList` | External (`assets/js/hooks/`) | Drag-and-drop reordering of record set items (uses sortablejs) |
|
| `SortableList` | External (`assets/js/hooks/`) | Drag-and-drop reordering of record set items (uses sortablejs) |
|
||||||
| Various `.ColocatedHooks` | Colocated (in .heex) | Inline hooks prefixed with `.` (includes `.ScrollBottom` for RecordChat) |
|
| Various `.ColocatedHooks` | Colocated (in .heex) | Inline hooks prefixed with `.` (includes `.ScrollBottom` for Chat) |
|
||||||
|
|
||||||
### JS Event Listeners (app.js)
|
### JS Event Listeners (app.js)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
defmodule MusicLibrary.ArtistChat do
|
||||||
|
@behaviour MusicLibrary.Chat
|
||||||
|
|
||||||
|
alias MusicLibrary.Artists.ArtistInfo
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def stream_response(messages, {artist, artist_info}, callback) do
|
||||||
|
instructions = build_instructions(artist, artist_info)
|
||||||
|
|
||||||
|
OpenAI.chat_stream(messages, on_chunk: callback, instructions: instructions)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_instructions(artist, artist_info) do
|
||||||
|
context = build_context(artist, artist_info)
|
||||||
|
|
||||||
|
"""
|
||||||
|
You are a knowledgeable music assistant. Answer questions about the artist \
|
||||||
|
the user is currently viewing. Use the provided artist information as your \
|
||||||
|
primary reference, and use web search to find additional up-to-date \
|
||||||
|
information when helpful. Be concise and accurate. When unsure, say so.
|
||||||
|
|
||||||
|
Artist information:
|
||||||
|
#{context}
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_context(artist, artist_info) do
|
||||||
|
country = ArtistInfo.country(artist_info)
|
||||||
|
description = ArtistInfo.wikipedia_description(artist_info)
|
||||||
|
summary = ArtistInfo.wikipedia_summary(artist_info)
|
||||||
|
|
||||||
|
parts =
|
||||||
|
[
|
||||||
|
{"Name", artist.name},
|
||||||
|
{"Country", country.name},
|
||||||
|
{"Description", description},
|
||||||
|
{"Biography", summary}
|
||||||
|
]
|
||||||
|
|> Enum.reject(fn {_label, value} -> value in [nil, ""] end)
|
||||||
|
|> Enum.map_join("\n", fn {label, value} -> "#{label}: #{value}" end)
|
||||||
|
|
||||||
|
parts
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
defmodule MusicLibrary.Chat do
|
||||||
|
@callback stream_response(
|
||||||
|
messages :: list(map()),
|
||||||
|
context :: term(),
|
||||||
|
callback :: (String.t() -> any())
|
||||||
|
) :: :ok | {:error, term()}
|
||||||
|
end
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
defmodule MusicLibrary.RecordChat do
|
defmodule MusicLibrary.RecordChat do
|
||||||
|
@behaviour MusicLibrary.Chat
|
||||||
|
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
|
|
||||||
def stream_response(messages, record, embedding_text, callback) do
|
@impl true
|
||||||
|
def stream_response(messages, {record, embedding_text}, callback) do
|
||||||
instructions = build_instructions(record, embedding_text)
|
instructions = build_instructions(record, embedding_text)
|
||||||
|
|
||||||
OpenAI.chat_stream(messages, on_chunk: callback, instructions: instructions)
|
OpenAI.chat_stream(messages, on_chunk: callback, instructions: instructions)
|
||||||
|
|||||||
+8
-12
@@ -1,9 +1,8 @@
|
|||||||
defmodule MusicLibraryWeb.Components.RecordChat do
|
defmodule MusicLibraryWeb.Components.Chat do
|
||||||
use MusicLibraryWeb, :live_component
|
use MusicLibraryWeb, :live_component
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias MusicLibrary.RecordChat
|
|
||||||
alias MusicLibraryWeb.Markdown
|
alias MusicLibraryWeb.Markdown
|
||||||
|
|
||||||
def open(id), do: Fluxon.open_dialog(id)
|
def open(id), do: Fluxon.open_dialog(id)
|
||||||
@@ -55,7 +54,7 @@ defmodule MusicLibraryWeb.Components.RecordChat do
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-2 pb-4 border-b border-zinc-200 dark:border-zinc-700">
|
<div class="flex items-center gap-2 pb-4 border-b border-zinc-200 dark:border-zinc-700">
|
||||||
<h2 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
<h2 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
||||||
{gettext("Chat about %{title}", title: @record.title)}
|
{gettext("Chat about %{title}", title: @title)}
|
||||||
</h2>
|
</h2>
|
||||||
<.button
|
<.button
|
||||||
:if={@messages != []}
|
:if={@messages != []}
|
||||||
@@ -82,10 +81,7 @@ defmodule MusicLibraryWeb.Components.RecordChat do
|
|||||||
name="hero-chat-bubble-left-right"
|
name="hero-chat-bubble-left-right"
|
||||||
class="size-12 mb-4 text-zinc-300 dark:text-zinc-600"
|
class="size-12 mb-4 text-zinc-300 dark:text-zinc-600"
|
||||||
/>
|
/>
|
||||||
<p class="text-sm font-medium">{gettext("Ask anything about this album")}</p>
|
<p class="text-sm font-medium">{@empty_prompt}</p>
|
||||||
<p class="text-xs mt-1">
|
|
||||||
{gettext("e.g. \"What genre is this?\", \"Tell me about the artists\"")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -144,7 +140,7 @@ defmodule MusicLibraryWeb.Components.RecordChat do
|
|||||||
<.input
|
<.input
|
||||||
name="message"
|
name="message"
|
||||||
value=""
|
value=""
|
||||||
placeholder={gettext("Ask about this album...")}
|
placeholder={@placeholder}
|
||||||
class="flex-1"
|
class="flex-1"
|
||||||
disabled={@loading}
|
disabled={@loading}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@@ -215,11 +211,11 @@ defmodule MusicLibraryWeb.Components.RecordChat do
|
|||||||
user_message = %{role: "user", content: String.trim(text)}
|
user_message = %{role: "user", content: String.trim(text)}
|
||||||
|
|
||||||
messages = socket.assigns.messages ++ [user_message]
|
messages = socket.assigns.messages ++ [user_message]
|
||||||
record = socket.assigns.record
|
chat_module = socket.assigns.chat_module
|
||||||
embedding_text = socket.assigns.embedding_text
|
chat_context = socket.assigns.chat_context
|
||||||
|
|
||||||
Task.Supervisor.start_child(MusicLibrary.TaskSupervisor, fn ->
|
Task.Supervisor.start_child(MusicLibrary.TaskSupervisor, fn ->
|
||||||
case RecordChat.stream_response(messages, record, embedding_text, fn chunk ->
|
case chat_module.stream_response(messages, chat_context, fn chunk ->
|
||||||
Phoenix.LiveView.send_update(parent_pid, __MODULE__,
|
Phoenix.LiveView.send_update(parent_pid, __MODULE__,
|
||||||
id: component_id,
|
id: component_id,
|
||||||
chunk: chunk
|
chunk: chunk
|
||||||
@@ -232,7 +228,7 @@ defmodule MusicLibraryWeb.Components.RecordChat do
|
|||||||
)
|
)
|
||||||
|
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
Logger.error("RecordChat streaming error: #{reason}")
|
Logger.error("Chat streaming error: #{reason}")
|
||||||
|
|
||||||
Phoenix.LiveView.send_update(parent_pid, __MODULE__,
|
Phoenix.LiveView.send_update(parent_pid, __MODULE__,
|
||||||
id: component_id,
|
id: component_id,
|
||||||
@@ -21,6 +21,18 @@
|
|||||||
data-slot="icon"
|
data-slot="icon"
|
||||||
/>
|
/>
|
||||||
</.button>
|
</.button>
|
||||||
|
<.button
|
||||||
|
variant="soft"
|
||||||
|
phx-click={MusicLibraryWeb.Components.Chat.open("artist-chat-sheet")}
|
||||||
|
>
|
||||||
|
<span class="sr-only">{gettext("Chat about artist")}</span>
|
||||||
|
<.icon
|
||||||
|
name="hero-chat-bubble-left-right"
|
||||||
|
class="h-5 w-5"
|
||||||
|
aria-hidden="true"
|
||||||
|
data-slot="icon"
|
||||||
|
/>
|
||||||
|
</.button>
|
||||||
<.dropdown id={"actions-#{@artist.musicbrainz_id}"} placement="bottom-end">
|
<.dropdown id={"actions-#{@artist.musicbrainz_id}"} placement="bottom-end">
|
||||||
<:toggle>
|
<:toggle>
|
||||||
<.button variant="soft">
|
<.button variant="soft">
|
||||||
@@ -332,6 +344,17 @@
|
|||||||
musicbrainz_id={@artist.musicbrainz_id}
|
musicbrainz_id={@artist.musicbrainz_id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<.live_component
|
||||||
|
id="artist-chat"
|
||||||
|
sheet_id="artist-chat-sheet"
|
||||||
|
module={MusicLibraryWeb.Components.Chat}
|
||||||
|
title={@artist.name}
|
||||||
|
chat_module={MusicLibrary.ArtistChat}
|
||||||
|
chat_context={{@artist, @artist_info}}
|
||||||
|
placeholder={gettext("Ask about this artist...")}
|
||||||
|
empty_prompt={gettext("Ask anything about this artist")}
|
||||||
|
/>
|
||||||
|
|
||||||
<.structured_modal
|
<.structured_modal
|
||||||
:if={@live_action == :edit}
|
:if={@live_action == :edit}
|
||||||
id="artist-info-modal"
|
id="artist-info-modal"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
</.button>
|
</.button>
|
||||||
<.button
|
<.button
|
||||||
variant="soft"
|
variant="soft"
|
||||||
phx-click={MusicLibraryWeb.Components.RecordChat.open("record-chat-sheet")}
|
phx-click={MusicLibraryWeb.Components.Chat.open("record-chat-sheet")}
|
||||||
>
|
>
|
||||||
<span class="sr-only">{gettext("Chat about album")}</span>
|
<span class="sr-only">{gettext("Chat about album")}</span>
|
||||||
<.icon
|
<.icon
|
||||||
@@ -342,9 +342,12 @@
|
|||||||
<.live_component
|
<.live_component
|
||||||
id="record-chat"
|
id="record-chat"
|
||||||
sheet_id="record-chat-sheet"
|
sheet_id="record-chat-sheet"
|
||||||
module={MusicLibraryWeb.Components.RecordChat}
|
module={MusicLibraryWeb.Components.Chat}
|
||||||
record={@record}
|
title={@record.title}
|
||||||
embedding_text={@embedding_text}
|
chat_module={MusicLibrary.RecordChat}
|
||||||
|
chat_context={{@record, @embedding_text}}
|
||||||
|
placeholder={gettext("Ask about this album...")}
|
||||||
|
empty_prompt={gettext("Ask anything about this album")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<.structured_modal
|
<.structured_modal
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
<.button_group>
|
<.button_group>
|
||||||
<.button
|
<.button
|
||||||
variant="soft"
|
variant="soft"
|
||||||
phx-click={MusicLibraryWeb.Components.RecordChat.open("record-chat-sheet")}
|
phx-click={MusicLibraryWeb.Components.Chat.open("record-chat-sheet")}
|
||||||
>
|
>
|
||||||
<span class="sr-only">{gettext("Chat about album")}</span>
|
<span class="sr-only">{gettext("Chat about album")}</span>
|
||||||
<.icon
|
<.icon
|
||||||
@@ -307,9 +307,12 @@
|
|||||||
<.live_component
|
<.live_component
|
||||||
id="record-chat"
|
id="record-chat"
|
||||||
sheet_id="record-chat-sheet"
|
sheet_id="record-chat-sheet"
|
||||||
module={MusicLibraryWeb.Components.RecordChat}
|
module={MusicLibraryWeb.Components.Chat}
|
||||||
record={@record}
|
title={@record.title}
|
||||||
embedding_text={@embedding_text}
|
chat_module={MusicLibrary.RecordChat}
|
||||||
|
chat_context={{@record, @embedding_text}}
|
||||||
|
placeholder={gettext("Ask about this album...")}
|
||||||
|
empty_prompt={gettext("Ask anything about this album")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<.structured_modal
|
<.structured_modal
|
||||||
|
|||||||
+25
-13
@@ -1951,7 +1951,7 @@ msgstr ""
|
|||||||
msgid "Record sets"
|
msgid "Record sets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#: lib/music_library_web/components/release.ex
|
#: lib/music_library_web/components/release.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Retry"
|
msgid "Retry"
|
||||||
@@ -1962,17 +1962,19 @@ msgstr ""
|
|||||||
msgid "Based on genres, artists, and musical style"
|
msgid "Based on genres, artists, and musical style"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/live/collection_live/show.html.heex
|
||||||
|
#: lib/music_library_web/live/wishlist_live/show.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Ask about this album..."
|
msgid "Ask about this album..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/live/collection_live/show.html.heex
|
||||||
|
#: lib/music_library_web/live/wishlist_live/show.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Ask anything about this album"
|
msgid "Ask anything about this album"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Chat about %{title}"
|
msgid "Chat about %{title}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1983,27 +1985,37 @@ msgstr ""
|
|||||||
msgid "Chat about album"
|
msgid "Chat about album"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Clear chat"
|
msgid "Clear chat"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Send message"
|
msgid "Send message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Thinking..."
|
msgid "Thinking..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "e.g. \"What genre is this?\", \"Tell me about the artists\""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Something went wrong. Please try again."
|
msgid "Something went wrong. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/artist_live/show.html.heex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Ask about this artist..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/artist_live/show.html.heex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Ask anything about this artist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/artist_live/show.html.heex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Chat about artist"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -1951,7 +1951,7 @@ msgstr ""
|
|||||||
msgid "Record sets"
|
msgid "Record sets"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#: lib/music_library_web/components/release.ex
|
#: lib/music_library_web/components/release.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Retry"
|
msgid "Retry"
|
||||||
@@ -1962,17 +1962,19 @@ msgstr ""
|
|||||||
msgid "Based on genres, artists, and musical style"
|
msgid "Based on genres, artists, and musical style"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/live/collection_live/show.html.heex
|
||||||
|
#: lib/music_library_web/live/wishlist_live/show.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Ask about this album..."
|
msgid "Ask about this album..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/live/collection_live/show.html.heex
|
||||||
|
#: lib/music_library_web/live/wishlist_live/show.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Ask anything about this album"
|
msgid "Ask anything about this album"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Chat about %{title}"
|
msgid "Chat about %{title}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1983,27 +1985,37 @@ msgstr ""
|
|||||||
msgid "Chat about album"
|
msgid "Chat about album"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Clear chat"
|
msgid "Clear chat"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Send message"
|
msgid "Send message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Thinking..."
|
msgid "Thinking..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
#: lib/music_library_web/components/chat.ex
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "e.g. \"What genre is this?\", \"Tell me about the artists\""
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_chat.ex
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Something went wrong. Please try again."
|
msgid "Something went wrong. Please try again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/artist_live/show.html.heex
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Ask about this artist..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/artist_live/show.html.heex
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Ask anything about this artist"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/artist_live/show.html.heex
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Chat about artist"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
Reference in New Issue
Block a user